+ * Performs authorization check based on the attributes associated with the
+ * incoming request, and returns status `OK` or not `OK`.
+ *
+ */
+ default void check(io.envoyproxy.envoy.service.auth.v3.CheckRequest request,
+ io.grpc.stub.StreamObserver responseObserver) {
+ io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getCheckMethod(), responseObserver);
+ }
+ }
+
+ /**
+ * Base class for the server implementation of the service Authorization.
+ *
+ * A generic interface for performing authorization check on incoming
+ * requests to a networked service.
+ *
+ */
+ public static abstract class AuthorizationImplBase
+ implements io.grpc.BindableService, AsyncService {
+
+ @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+ return AuthorizationGrpc.bindService(this);
+ }
+ }
+
+ /**
+ * A stub to allow clients to do asynchronous rpc calls to service Authorization.
+ *
+ * A generic interface for performing authorization check on incoming
+ * requests to a networked service.
+ *
+ * Performs authorization check based on the attributes associated with the
+ * incoming request, and returns status `OK` or not `OK`.
+ *
+ */
+ public com.google.common.util.concurrent.ListenableFuture check(
+ io.envoyproxy.envoy.service.auth.v3.CheckRequest request) {
+ return io.grpc.stub.ClientCalls.futureUnaryCall(
+ getChannel().newCall(getCheckMethod(), getCallOptions()), request);
+ }
+ }
+
+ private static final int METHODID_CHECK = 0;
+
+ private static final class MethodHandlers implements
+ io.grpc.stub.ServerCalls.UnaryMethod,
+ io.grpc.stub.ServerCalls.ServerStreamingMethod,
+ io.grpc.stub.ServerCalls.ClientStreamingMethod,
+ io.grpc.stub.ServerCalls.BidiStreamingMethod {
+ private final AsyncService serviceImpl;
+ private final int methodId;
+
+ MethodHandlers(AsyncService serviceImpl, int methodId) {
+ this.serviceImpl = serviceImpl;
+ this.methodId = methodId;
+ }
+
+ @java.lang.Override
+ @java.lang.SuppressWarnings("unchecked")
+ public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) {
+ switch (methodId) {
+ case METHODID_CHECK:
+ serviceImpl.check((io.envoyproxy.envoy.service.auth.v3.CheckRequest) request,
+ (io.grpc.stub.StreamObserver) responseObserver);
+ break;
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ @java.lang.Override
+ @java.lang.SuppressWarnings("unchecked")
+ public io.grpc.stub.StreamObserver invoke(
+ io.grpc.stub.StreamObserver responseObserver) {
+ switch (methodId) {
+ default:
+ throw new AssertionError();
+ }
+ }
+ }
+
+ public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) {
+ return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+ .addMethod(
+ getCheckMethod(),
+ io.grpc.stub.ServerCalls.asyncUnaryCall(
+ new MethodHandlers<
+ io.envoyproxy.envoy.service.auth.v3.CheckRequest,
+ io.envoyproxy.envoy.service.auth.v3.CheckResponse>(
+ service, METHODID_CHECK)))
+ .build();
+ }
+
+ private static abstract class AuthorizationBaseDescriptorSupplier
+ implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
+ AuthorizationBaseDescriptorSupplier() {}
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
+ return io.envoyproxy.envoy.service.auth.v3.ExternalAuthProto.getDescriptor();
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
+ return getFileDescriptor().findServiceByName("Authorization");
+ }
+ }
+
+ private static final class AuthorizationFileDescriptorSupplier
+ extends AuthorizationBaseDescriptorSupplier {
+ AuthorizationFileDescriptorSupplier() {}
+ }
+
+ private static final class AuthorizationMethodDescriptorSupplier
+ extends AuthorizationBaseDescriptorSupplier
+ implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
+ private final java.lang.String methodName;
+
+ AuthorizationMethodDescriptorSupplier(java.lang.String methodName) {
+ this.methodName = methodName;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
+ return getServiceDescriptor().findMethodByName(methodName);
+ }
+ }
+
+ private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
+
+ public static io.grpc.ServiceDescriptor getServiceDescriptor() {
+ io.grpc.ServiceDescriptor result = serviceDescriptor;
+ if (result == null) {
+ synchronized (AuthorizationGrpc.class) {
+ result = serviceDescriptor;
+ if (result == null) {
+ serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
+ .setSchemaDescriptor(new AuthorizationFileDescriptorSupplier())
+ .addMethod(getCheckMethod())
+ .build();
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/xds/src/main/java/io/grpc/xds/internal/extauthz/ExtAuthzConfig.java b/xds/src/main/java/io/grpc/xds/internal/extauthz/ExtAuthzConfig.java
new file mode 100644
index 00000000000..e826f501d9c
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/internal/extauthz/ExtAuthzConfig.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.extauthz;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import io.envoyproxy.envoy.config.common.mutation_rules.v3.HeaderMutationRules;
+import io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3.ExtAuthz;
+import io.grpc.Status;
+import io.grpc.internal.GrpcUtil;
+import io.grpc.xds.internal.MatcherParser;
+import io.grpc.xds.internal.Matchers;
+import io.grpc.xds.internal.grpcservice.GrpcServiceConfig;
+import io.grpc.xds.internal.grpcservice.GrpcServiceParseException;
+import io.grpc.xds.internal.headermutations.HeaderMutationRulesConfig;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Represents the configuration for the external authorization (ext_authz) filter. This class
+ * encapsulates the settings defined in the
+ * {@link io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3.ExtAuthz} proto, providing a
+ * structured, immutable representation for use within gRPC. It includes configurations for the gRPC
+ * service used for authorization, header mutation rules, and other filter behaviors.
+ */
+@AutoValue
+public abstract class ExtAuthzConfig {
+
+ /** Creates a new builder for creating {@link ExtAuthzConfig} instances. */
+ public static Builder builder() {
+ return new AutoValue_ExtAuthzConfig.Builder().allowedHeaders(ImmutableList.of())
+ .disallowedHeaders(ImmutableList.of()).statusOnError(Status.PERMISSION_DENIED)
+ .filterEnabled(Matchers.FractionMatcher.create(100, 100));
+ }
+
+ /**
+ * Parses the {@link io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3.ExtAuthz} proto to
+ * create an {@link ExtAuthzConfig} instance.
+ *
+ * @param extAuthzProto The ext_authz proto to parse.
+ * @return An {@link ExtAuthzConfig} instance.
+ * @throws ExtAuthzParseException if the proto is invalid or contains unsupported features.
+ */
+ public static ExtAuthzConfig fromProto(ExtAuthz extAuthzProto) throws ExtAuthzParseException {
+ if (!extAuthzProto.hasGrpcService()) {
+ throw new ExtAuthzParseException(
+ "unsupported ExtAuthz service type: only grpc_service is " + "supported");
+ }
+ GrpcServiceConfig grpcServiceConfig;
+ try {
+ grpcServiceConfig = GrpcServiceConfig.fromProto(extAuthzProto.getGrpcService());
+ } catch (GrpcServiceParseException e) {
+ throw new ExtAuthzParseException("Failed to parse GrpcService config: " + e.getMessage(), e);
+ }
+ Builder builder = builder().grpcService(grpcServiceConfig)
+ .failureModeAllow(extAuthzProto.getFailureModeAllow())
+ .failureModeAllowHeaderAdd(extAuthzProto.getFailureModeAllowHeaderAdd())
+ .includePeerCertificate(extAuthzProto.getIncludePeerCertificate())
+ .denyAtDisable(extAuthzProto.getDenyAtDisable().getDefaultValue().getValue());
+
+ if (extAuthzProto.hasFilterEnabled()) {
+ builder.filterEnabled(parsePercent(extAuthzProto.getFilterEnabled().getDefaultValue()));
+ }
+
+ if (extAuthzProto.hasStatusOnError()) {
+ builder.statusOnError(
+ GrpcUtil.httpStatusToGrpcStatus(extAuthzProto.getStatusOnError().getCodeValue()));
+ }
+
+ if (extAuthzProto.hasAllowedHeaders()) {
+ builder.allowedHeaders(extAuthzProto.getAllowedHeaders().getPatternsList().stream()
+ .map(MatcherParser::parseStringMatcher).collect(ImmutableList.toImmutableList()));
+ }
+
+ if (extAuthzProto.hasDisallowedHeaders()) {
+ builder.disallowedHeaders(extAuthzProto.getDisallowedHeaders().getPatternsList().stream()
+ .map(MatcherParser::parseStringMatcher).collect(ImmutableList.toImmutableList()));
+ }
+
+ if (extAuthzProto.hasDecoderHeaderMutationRules()) {
+ builder.decoderHeaderMutationRules(
+ parseHeaderMutationRules(extAuthzProto.getDecoderHeaderMutationRules()));
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * The gRPC service configuration for the external authorization service. This is a required
+ * field.
+ *
+ * @see ExtAuthz#getGrpcService()
+ */
+ public abstract GrpcServiceConfig grpcService();
+
+ /**
+ * Changes the filter's behavior on errors from the authorization service. If {@code true}, the
+ * filter will accept the request even if the authorization service fails or returns an error.
+ *
+ * @see ExtAuthz#getFailureModeAllow()
+ */
+ public abstract boolean failureModeAllow();
+
+ /**
+ * Determines if the {@code x-envoy-auth-failure-mode-allowed} header is added to the request when
+ * {@link #failureModeAllow()} is true.
+ *
+ * @see ExtAuthz#getFailureModeAllowHeaderAdd()
+ */
+ public abstract boolean failureModeAllowHeaderAdd();
+
+ /**
+ * Specifies if the peer certificate is sent to the external authorization service.
+ *
+ * @see ExtAuthz#getIncludePeerCertificate()
+ */
+ public abstract boolean includePeerCertificate();
+
+ /**
+ * The gRPC status returned to the client when the authorization server returns an error or is
+ * unreachable. Defaults to {@code PERMISSION_DENIED}.
+ *
+ * @see io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3.ExtAuthz#getStatusOnError()
+ */
+ public abstract Status statusOnError();
+
+ /**
+ * Specifies whether to deny requests when the filter is disabled. Defaults to {@code false}.
+ *
+ * @see ExtAuthz#getDenyAtDisable()
+ */
+ public abstract boolean denyAtDisable();
+
+ /**
+ * The fraction of requests that will be checked by the authorization service. Defaults to all
+ * requests.
+ *
+ * @see ExtAuthz#getFilterEnabled()
+ */
+ public abstract Matchers.FractionMatcher filterEnabled();
+
+ /**
+ * Specifies which request headers are sent to the authorization service. If not set, all headers
+ * are sent.
+ *
+ * @see ExtAuthz#getAllowedHeaders()
+ */
+ public abstract ImmutableList allowedHeaders();
+
+ /**
+ * Specifies which request headers are not sent to the authorization service. This overrides
+ * {@link #allowedHeaders()}.
+ *
+ * @see ExtAuthz#getDisallowedHeaders()
+ */
+ public abstract ImmutableList disallowedHeaders();
+
+ /**
+ * Rules for what modifications an ext_authz server may make to request headers.
+ *
+ * @see ExtAuthz#getDecoderHeaderMutationRules()
+ */
+ public abstract Optional decoderHeaderMutationRules();
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder grpcService(GrpcServiceConfig grpcService);
+
+ public abstract Builder failureModeAllow(boolean failureModeAllow);
+
+ public abstract Builder failureModeAllowHeaderAdd(boolean failureModeAllowHeaderAdd);
+
+ public abstract Builder includePeerCertificate(boolean includePeerCertificate);
+
+ public abstract Builder statusOnError(Status statusOnError);
+
+ public abstract Builder denyAtDisable(boolean denyAtDisable);
+
+ public abstract Builder filterEnabled(Matchers.FractionMatcher filterEnabled);
+
+ public abstract Builder allowedHeaders(Iterable allowedHeaders);
+
+ public abstract Builder disallowedHeaders(Iterable disallowedHeaders);
+
+ public abstract Builder decoderHeaderMutationRules(HeaderMutationRulesConfig rules);
+
+ public abstract ExtAuthzConfig build();
+ }
+
+
+ private static Matchers.FractionMatcher parsePercent(
+ io.envoyproxy.envoy.type.v3.FractionalPercent proto) throws ExtAuthzParseException {
+ int denominator;
+ switch (proto.getDenominator()) {
+ case HUNDRED:
+ denominator = 100;
+ break;
+ case TEN_THOUSAND:
+ denominator = 10_000;
+ break;
+ case MILLION:
+ denominator = 1_000_000;
+ break;
+ case UNRECOGNIZED:
+ default:
+ throw new ExtAuthzParseException("Unknown denominator type: " + proto.getDenominator());
+ }
+ return Matchers.FractionMatcher.create(proto.getNumerator(), denominator);
+ }
+
+ private static HeaderMutationRulesConfig parseHeaderMutationRules(HeaderMutationRules proto)
+ throws ExtAuthzParseException {
+ HeaderMutationRulesConfig.Builder builder = HeaderMutationRulesConfig.builder();
+ builder.disallowAll(proto.getDisallowAll().getValue());
+ builder.disallowIsError(proto.getDisallowIsError().getValue());
+ if (proto.hasAllowExpression()) {
+ builder.allowExpression(
+ parseRegex(proto.getAllowExpression().getRegex(), "allow_expression"));
+ }
+ if (proto.hasDisallowExpression()) {
+ builder.disallowExpression(
+ parseRegex(proto.getDisallowExpression().getRegex(), "disallow_expression"));
+ }
+ return builder.build();
+ }
+
+ private static Pattern parseRegex(String regex, String fieldName) throws ExtAuthzParseException {
+ try {
+ return Pattern.compile(regex);
+ } catch (PatternSyntaxException e) {
+ throw new ExtAuthzParseException(
+ "Invalid regex pattern for " + fieldName + ": " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/xds/src/main/java/io/grpc/xds/internal/extauthz/ExtAuthzParseException.java b/xds/src/main/java/io/grpc/xds/internal/extauthz/ExtAuthzParseException.java
new file mode 100644
index 00000000000..78edea5c305
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/internal/extauthz/ExtAuthzParseException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.extauthz;
+
+/**
+ * A custom exception for signaling errors during the parsing of external authorization
+ * (ext_authz) configurations.
+ */
+public class ExtAuthzParseException extends Exception {
+
+ private static final long serialVersionUID = 0L;
+
+ public ExtAuthzParseException(String message) {
+ super(message);
+ }
+
+ public ExtAuthzParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfig.java b/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfig.java
new file mode 100644
index 00000000000..da9be978f87
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfig.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.grpcservice;
+
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.OAuth2Credentials;
+import com.google.auto.value.AutoValue;
+import com.google.common.io.BaseEncoding;
+import com.google.protobuf.Any;
+import com.google.protobuf.InvalidProtocolBufferException;
+import io.envoyproxy.envoy.config.core.v3.GrpcService;
+import io.envoyproxy.envoy.extensions.grpc_service.call_credentials.access_token.v3.AccessTokenCredentials;
+import io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.xds.v3.XdsCredentials;
+import io.grpc.CallCredentials;
+import io.grpc.ChannelCredentials;
+import io.grpc.InsecureChannelCredentials;
+import io.grpc.Metadata;
+import io.grpc.alts.GoogleDefaultChannelCredentials;
+import io.grpc.auth.MoreCallCredentials;
+import io.grpc.xds.XdsChannelCredentials;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+
+/**
+ * A Java representation of the {@link io.envoyproxy.envoy.config.core.v3.GrpcService} proto,
+ * designed for parsing and internal use within gRPC. This class encapsulates the configuration for
+ * a gRPC service, including target URI, credentials, and other settings. The parsing logic adheres
+ * to the specifications outlined in
+ * A102: xDS GrpcService Support. This class is immutable and uses the AutoValue library for its
+ * implementation.
+ */
+@AutoValue
+public abstract class GrpcServiceConfig {
+
+ public static Builder builder() {
+ return new AutoValue_GrpcServiceConfig.Builder();
+ }
+
+ /**
+ * Parses the {@link io.envoyproxy.envoy.config.core.v3.GrpcService} proto to create a
+ * {@link GrpcServiceConfig} instance. This method adheres to gRFC A102, which specifies that only
+ * the {@code google_grpc} target specifier is supported. Other fields like {@code timeout} and
+ * {@code initial_metadata} are also parsed as per the gRFC.
+ *
+ * @param grpcServiceProto The proto to parse.
+ * @return A {@link GrpcServiceConfig} instance.
+ * @throws GrpcServiceParseException if the proto is invalid or uses unsupported features.
+ */
+ public static GrpcServiceConfig fromProto(GrpcService grpcServiceProto)
+ throws GrpcServiceParseException {
+ if (!grpcServiceProto.hasGoogleGrpc()) {
+ throw new GrpcServiceParseException(
+ "Unsupported: GrpcService must have GoogleGrpc, got: " + grpcServiceProto);
+ }
+ GoogleGrpcConfig googleGrpcConfig =
+ GoogleGrpcConfig.fromProto(grpcServiceProto.getGoogleGrpc());
+
+ Builder builder = GrpcServiceConfig.builder().googleGrpc(googleGrpcConfig);
+
+ if (!grpcServiceProto.getInitialMetadataList().isEmpty()) {
+ Metadata initialMetadata = new Metadata();
+ for (io.envoyproxy.envoy.config.core.v3.HeaderValue header : grpcServiceProto
+ .getInitialMetadataList()) {
+ String key = header.getKey();
+ if (key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
+ initialMetadata.put(Metadata.Key.of(key, Metadata.BINARY_BYTE_MARSHALLER),
+ BaseEncoding.base64().decode(header.getValue()));
+ } else {
+ initialMetadata.put(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER),
+ header.getValue());
+ }
+ }
+ builder.initialMetadata(initialMetadata);
+ }
+
+ if (grpcServiceProto.hasTimeout()) {
+ com.google.protobuf.Duration timeout = grpcServiceProto.getTimeout();
+ builder.timeout(Duration.ofSeconds(timeout.getSeconds(), timeout.getNanos()));
+ }
+ return builder.build();
+ }
+
+ public abstract GoogleGrpcConfig googleGrpc();
+
+ public abstract Optional timeout();
+
+ public abstract Optional initialMetadata();
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder googleGrpc(GoogleGrpcConfig googleGrpc);
+
+ public abstract Builder timeout(Duration timeout);
+
+ public abstract Builder initialMetadata(Metadata initialMetadata);
+
+ public abstract GrpcServiceConfig build();
+ }
+
+ /**
+ * Represents the configuration for a Google gRPC service, as defined in the
+ * {@link io.envoyproxy.envoy.config.core.v3.GrpcService.GoogleGrpc} proto. This class
+ * encapsulates settings specific to Google's gRPC implementation, such as target URI and
+ * credentials. The parsing of this configuration is guided by gRFC A102, which specifies how gRPC
+ * clients should interpret the GrpcService proto.
+ */
+ @AutoValue
+ public abstract static class GoogleGrpcConfig {
+
+ private static final String TLS_CREDENTIALS_TYPE_URL =
+ "type.googleapis.com/envoy.extensions.grpc_service.channel_credentials."
+ + "tls.v3.TlsCredentials";
+ private static final String LOCAL_CREDENTIALS_TYPE_URL =
+ "type.googleapis.com/envoy.extensions.grpc_service.channel_credentials."
+ + "local.v3.LocalCredentials";
+ private static final String XDS_CREDENTIALS_TYPE_URL =
+ "type.googleapis.com/envoy.extensions.grpc_service.channel_credentials."
+ + "xds.v3.XdsCredentials";
+ private static final String INSECURE_CREDENTIALS_TYPE_URL =
+ "type.googleapis.com/envoy.extensions.grpc_service.channel_credentials."
+ + "insecure.v3.InsecureCredentials";
+ private static final String GOOGLE_DEFAULT_CREDENTIALS_TYPE_URL =
+ "type.googleapis.com/envoy.extensions.grpc_service.channel_credentials."
+ + "google_default.v3.GoogleDefaultCredentials";
+
+ public static Builder builder() {
+ return new AutoValue_GrpcServiceConfig_GoogleGrpcConfig.Builder();
+ }
+
+ /**
+ * Parses the {@link io.envoyproxy.envoy.config.core.v3.GrpcService.GoogleGrpc} proto to create
+ * a {@link GoogleGrpcConfig} instance.
+ *
+ * @param googleGrpcProto The proto to parse.
+ * @return A {@link GoogleGrpcConfig} instance.
+ * @throws GrpcServiceParseException if the proto is invalid.
+ */
+ public static GoogleGrpcConfig fromProto(GrpcService.GoogleGrpc googleGrpcProto)
+ throws GrpcServiceParseException {
+
+ HashedChannelCredentials channelCreds =
+ extractChannelCredentials(googleGrpcProto.getChannelCredentialsPluginList());
+
+ CallCredentials callCreds =
+ extractCallCredentials(googleGrpcProto.getCallCredentialsPluginList());
+
+ return GoogleGrpcConfig.builder().target(googleGrpcProto.getTargetUri())
+ .hashedChannelCredentials(channelCreds).callCredentials(callCreds).build();
+ }
+
+ public abstract String target();
+
+ public abstract HashedChannelCredentials hashedChannelCredentials();
+
+ public abstract CallCredentials callCredentials();
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder target(String target);
+
+ public abstract Builder hashedChannelCredentials(HashedChannelCredentials channelCredentials);
+
+ public abstract Builder callCredentials(CallCredentials callCredentials);
+
+ public abstract GoogleGrpcConfig build();
+ }
+
+ private static T getFirstSupported(List configs, Parser parser,
+ String configName) throws GrpcServiceParseException {
+ List errors = new ArrayList<>();
+ for (U config : configs) {
+ try {
+ return parser.parse(config);
+ } catch (GrpcServiceParseException e) {
+ errors.add(e.getMessage());
+ }
+ }
+ throw new GrpcServiceParseException(
+ "No valid supported " + configName + " found. Errors: " + errors);
+ }
+
+ private static HashedChannelCredentials channelCredsFromProto(Any cred)
+ throws GrpcServiceParseException {
+ String typeUrl = cred.getTypeUrl();
+ try {
+ switch (typeUrl) {
+ case GOOGLE_DEFAULT_CREDENTIALS_TYPE_URL:
+ return HashedChannelCredentials.of(GoogleDefaultChannelCredentials.create(),
+ cred.hashCode());
+ case INSECURE_CREDENTIALS_TYPE_URL:
+ return HashedChannelCredentials.of(InsecureChannelCredentials.create(),
+ cred.hashCode());
+ case XDS_CREDENTIALS_TYPE_URL:
+ XdsCredentials xdsConfig = cred.unpack(XdsCredentials.class);
+ HashedChannelCredentials fallbackCreds =
+ channelCredsFromProto(xdsConfig.getFallbackCredentials());
+ return HashedChannelCredentials.of(
+ XdsChannelCredentials.create(fallbackCreds.channelCredentials()), cred.hashCode());
+ case LOCAL_CREDENTIALS_TYPE_URL:
+ // TODO(sauravzg) : What's the java alternative to LocalCredentials.
+ throw new GrpcServiceParseException("LocalCredentials are not yet supported.");
+ case TLS_CREDENTIALS_TYPE_URL:
+ // TODO(sauravzg) : How to instantiate a TlsChannelCredentials from TlsCredentials
+ // proto?
+ throw new GrpcServiceParseException("TlsCredentials are not yet supported.");
+ default:
+ throw new GrpcServiceParseException("Unsupported channel credentials type: " + typeUrl);
+ }
+ } catch (InvalidProtocolBufferException e) {
+ // TODO(sauravzg): Add unit tests when we have a solution for TLS creds.
+ // This code is as of writing unreachable because all channel credential message
+ // types except TLS are empty messages.
+ throw new GrpcServiceParseException(
+ "Failed to parse channel credentials: " + e.getMessage());
+ }
+ }
+
+ private static CallCredentials callCredsFromProto(Any cred) throws GrpcServiceParseException {
+ try {
+ AccessTokenCredentials accessToken = cred.unpack(AccessTokenCredentials.class);
+ // TODO(sauravzg): Verify if the current behavior is per spec.The `AccessTokenCredentials`
+ // config doesn't have any timeout/refresh, so set the token to never expire.
+ return MoreCallCredentials.from(OAuth2Credentials
+ .create(new AccessToken(accessToken.getToken(), new Date(Long.MAX_VALUE))));
+ } catch (InvalidProtocolBufferException e) {
+ throw new GrpcServiceParseException(
+ "Unsupported call credentials type: " + cred.getTypeUrl());
+ }
+ }
+
+ private static HashedChannelCredentials extractChannelCredentials(
+ List channelCredentialPlugins) throws GrpcServiceParseException {
+ return getFirstSupported(channelCredentialPlugins, GoogleGrpcConfig::channelCredsFromProto,
+ "channel_credentials");
+ }
+
+ private static CallCredentials extractCallCredentials(List callCredentialPlugins)
+ throws GrpcServiceParseException {
+ return getFirstSupported(callCredentialPlugins, GoogleGrpcConfig::callCredsFromProto,
+ "call_credentials");
+ }
+ }
+
+ /**
+ * A container for {@link ChannelCredentials} and a hash for the purpose of caching.
+ */
+ @AutoValue
+ public abstract static class HashedChannelCredentials {
+ /**
+ * Creates a new {@link HashedChannelCredentials} instance.
+ *
+ * @param creds The channel credentials.
+ * @param hash The hash of the credentials.
+ * @return A new {@link HashedChannelCredentials} instance.
+ */
+ public static HashedChannelCredentials of(ChannelCredentials creds, int hash) {
+ return new AutoValue_GrpcServiceConfig_HashedChannelCredentials(creds, hash);
+ }
+
+ /**
+ * Returns the channel credentials.
+ */
+ public abstract ChannelCredentials channelCredentials();
+
+ /**
+ * Returns the hash of the credentials.
+ */
+ public abstract int hash();
+ }
+
+ /**
+ * Defines a generic interface for parsing a configuration of type {@code U} into a result of type
+ * {@code T}. This functional interface is used to abstract the parsing logic for different parts
+ * of the GrpcService configuration.
+ *
+ * @param The type of the object that will be returned after parsing.
+ * @param The type of the configuration object that will be parsed.
+ */
+ private interface Parser {
+
+ /**
+ * Parses the given configuration.
+ *
+ * @param config The configuration object to parse.
+ * @return The parsed object of type {@code T}.
+ * @throws GrpcServiceParseException if an error occurs during parsing.
+ */
+ T parse(U config) throws GrpcServiceParseException;
+ }
+}
diff --git a/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfigChannelFactory.java b/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfigChannelFactory.java
new file mode 100644
index 00000000000..0d02989eaa3
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfigChannelFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.grpcservice;
+
+import io.grpc.ManagedChannel;
+
+/**
+ * A factory for creating {@link ManagedChannel}s from a {@link GrpcServiceConfig}.
+ */
+public interface GrpcServiceConfigChannelFactory {
+ ManagedChannel createChannel(GrpcServiceConfig config);
+}
diff --git a/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceParseException.java b/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceParseException.java
new file mode 100644
index 00000000000..319ad3d07e3
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceParseException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.grpcservice;
+
+/**
+ * Exception thrown when there is an error parsing the gRPC service config.
+ */
+public class GrpcServiceParseException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public GrpcServiceParseException(String message) {
+ super(message);
+ }
+
+ public GrpcServiceParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/xds/src/main/java/io/grpc/xds/internal/grpcservice/InsecureGrpcChannelFactory.java b/xds/src/main/java/io/grpc/xds/internal/grpcservice/InsecureGrpcChannelFactory.java
new file mode 100644
index 00000000000..d6325d43be4
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/internal/grpcservice/InsecureGrpcChannelFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.grpcservice;
+
+import io.grpc.Grpc;
+import io.grpc.ManagedChannel;
+
+/**
+ * An insecure implementation of {@link GrpcServiceConfigChannelFactory} that creates a plaintext
+ * channel. This is a stub implementation for channel creation until the GrpcService trusted server
+ * implementation is completely implemented.
+ */
+public final class InsecureGrpcChannelFactory implements GrpcServiceConfigChannelFactory {
+
+ private static final InsecureGrpcChannelFactory INSTANCE = new InsecureGrpcChannelFactory();
+
+ private InsecureGrpcChannelFactory() {}
+
+ public static InsecureGrpcChannelFactory getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public ManagedChannel createChannel(GrpcServiceConfig config) {
+ GrpcServiceConfig.GoogleGrpcConfig googleGrpc = config.googleGrpc();
+ return Grpc.newChannelBuilder(googleGrpc.target(),
+ googleGrpc.hashedChannelCredentials().channelCredentials()).build();
+ }
+}
diff --git a/xds/src/main/java/io/grpc/xds/internal/headermutations/HeaderMutationRulesConfig.java b/xds/src/main/java/io/grpc/xds/internal/headermutations/HeaderMutationRulesConfig.java
new file mode 100644
index 00000000000..fd8048fdbd2
--- /dev/null
+++ b/xds/src/main/java/io/grpc/xds/internal/headermutations/HeaderMutationRulesConfig.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.headermutations;
+
+import com.google.auto.value.AutoValue;
+import io.envoyproxy.envoy.config.common.mutation_rules.v3.HeaderMutationRules;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+/**
+ * Represents the configuration for header mutation rules, as defined in the
+ * {@link io.envoyproxy.envoy.config.common.mutation_rules.v3.HeaderMutationRules} proto.
+ */
+@AutoValue
+public abstract class HeaderMutationRulesConfig {
+ /** Creates a new builder for creating {@link HeaderMutationRulesConfig} instances. */
+ public static Builder builder() {
+ return new AutoValue_HeaderMutationRulesConfig.Builder().disallowAll(false)
+ .disallowIsError(false);
+ }
+
+ /**
+ * If set, allows any header that matches this regular expression.
+ *
+ * @see HeaderMutationRules#getAllowExpression()
+ */
+ public abstract Optional allowExpression();
+
+ /**
+ * If set, disallows any header that matches this regular expression.
+ *
+ * @see HeaderMutationRules#getDisallowExpression()
+ */
+ public abstract Optional disallowExpression();
+
+ /**
+ * If true, disallows all header mutations.
+ *
+ * @see HeaderMutationRules#getDisallowAll()
+ */
+ public abstract boolean disallowAll();
+
+ /**
+ * If true, disallows any header mutation that would result in an invalid header value.
+ *
+ * @see HeaderMutationRules#getDisallowIsError()
+ */
+ public abstract boolean disallowIsError();
+
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder allowExpression(Pattern matcher);
+
+ public abstract Builder disallowExpression(Pattern matcher);
+
+ public abstract Builder disallowAll(boolean disallowAll);
+
+ public abstract Builder disallowIsError(boolean disallowIsError);
+
+ public abstract HeaderMutationRulesConfig build();
+ }
+}
diff --git a/xds/src/test/java/io/grpc/xds/internal/extauthz/ExtAuthzConfigTest.java b/xds/src/test/java/io/grpc/xds/internal/extauthz/ExtAuthzConfigTest.java
new file mode 100644
index 00000000000..9b9a55b4079
--- /dev/null
+++ b/xds/src/test/java/io/grpc/xds/internal/extauthz/ExtAuthzConfigTest.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.extauthz;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.protobuf.Any;
+import com.google.protobuf.BoolValue;
+import io.envoyproxy.envoy.config.common.mutation_rules.v3.HeaderMutationRules;
+import io.envoyproxy.envoy.config.core.v3.HeaderValue;
+import io.envoyproxy.envoy.config.core.v3.RuntimeFeatureFlag;
+import io.envoyproxy.envoy.config.core.v3.RuntimeFractionalPercent;
+import io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3.ExtAuthz;
+import io.envoyproxy.envoy.extensions.grpc_service.call_credentials.access_token.v3.AccessTokenCredentials;
+import io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.google_default.v3.GoogleDefaultCredentials;
+import io.envoyproxy.envoy.type.matcher.v3.ListStringMatcher;
+import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher;
+import io.envoyproxy.envoy.type.matcher.v3.StringMatcher;
+import io.envoyproxy.envoy.type.v3.FractionalPercent;
+import io.envoyproxy.envoy.type.v3.FractionalPercent.DenominatorType;
+import io.grpc.Status;
+import io.grpc.xds.internal.Matchers;
+import io.grpc.xds.internal.headermutations.HeaderMutationRulesConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ExtAuthzConfigTest {
+
+ private static final Any GOOGLE_DEFAULT_CHANNEL_CREDS =
+ Any.pack(GoogleDefaultCredentials.newBuilder().build());
+ private static final Any FAKE_ACCESS_TOKEN_CALL_CREDS =
+ Any.pack(AccessTokenCredentials.newBuilder().build());
+
+ private ExtAuthz.Builder extAuthzBuilder;
+
+ @Before
+ public void setUp() {
+ extAuthzBuilder = ExtAuthz.newBuilder()
+ .setGrpcService(io.envoyproxy.envoy.config.core.v3.GrpcService.newBuilder()
+ .setGoogleGrpc(io.envoyproxy.envoy.config.core.v3.GrpcService.GoogleGrpc.newBuilder()
+ .setTargetUri("test-cluster")
+ .addChannelCredentialsPlugin(GOOGLE_DEFAULT_CHANNEL_CREDS)
+ .addCallCredentialsPlugin(FAKE_ACCESS_TOKEN_CALL_CREDS).build())
+ .build());
+ }
+
+ @Test
+ public void fromProto_missingGrpcService_throws() {
+ ExtAuthz extAuthz = ExtAuthz.newBuilder().build();
+ try {
+ ExtAuthzConfig.fromProto(extAuthz);
+ fail("Expected ExtAuthzParseException");
+ } catch (ExtAuthzParseException e) {
+ assertThat(e).hasMessageThat()
+ .isEqualTo("unsupported ExtAuthz service type: only grpc_service is supported");
+ }
+ }
+
+ @Test
+ public void fromProto_invalidGrpcService_throws() {
+ ExtAuthz extAuthz = ExtAuthz.newBuilder()
+ .setGrpcService(io.envoyproxy.envoy.config.core.v3.GrpcService.newBuilder().build())
+ .build();
+ try {
+ ExtAuthzConfig.fromProto(extAuthz);
+ fail("Expected ExtAuthzParseException");
+ } catch (ExtAuthzParseException e) {
+ assertThat(e).hasMessageThat().startsWith("Failed to parse GrpcService config:");
+ }
+ }
+
+ @Test
+ public void fromProto_invalidAllowExpression_throws() {
+ ExtAuthz extAuthz = extAuthzBuilder
+ .setDecoderHeaderMutationRules(HeaderMutationRules.newBuilder()
+ .setAllowExpression(RegexMatcher.newBuilder().setRegex("[invalid").build()).build())
+ .build();
+ try {
+ ExtAuthzConfig.fromProto(extAuthz);
+ fail("Expected ExtAuthzParseException");
+ } catch (ExtAuthzParseException e) {
+ assertThat(e).hasMessageThat().startsWith("Invalid regex pattern for allow_expression:");
+ }
+ }
+
+ @Test
+ public void fromProto_invalidDisallowExpression_throws() {
+ ExtAuthz extAuthz = extAuthzBuilder
+ .setDecoderHeaderMutationRules(HeaderMutationRules.newBuilder()
+ .setDisallowExpression(RegexMatcher.newBuilder().setRegex("[invalid").build()).build())
+ .build();
+ try {
+ ExtAuthzConfig.fromProto(extAuthz);
+ fail("Expected ExtAuthzParseException");
+ } catch (ExtAuthzParseException e) {
+ assertThat(e).hasMessageThat().startsWith("Invalid regex pattern for disallow_expression:");
+ }
+ }
+
+ @Test
+ public void fromProto_success() throws ExtAuthzParseException {
+ ExtAuthz extAuthz = extAuthzBuilder
+ .setGrpcService(extAuthzBuilder.getGrpcServiceBuilder()
+ .setTimeout(com.google.protobuf.Duration.newBuilder().setSeconds(5).build())
+ .addInitialMetadata(HeaderValue.newBuilder().setKey("key").setValue("value").build())
+ .build())
+ .setFailureModeAllow(true).setFailureModeAllowHeaderAdd(true)
+ .setIncludePeerCertificate(true)
+ .setStatusOnError(
+ io.envoyproxy.envoy.type.v3.HttpStatus.newBuilder().setCodeValue(403).build())
+ .setDenyAtDisable(
+ RuntimeFeatureFlag.newBuilder().setDefaultValue(BoolValue.of(true)).build())
+ .setFilterEnabled(RuntimeFractionalPercent.newBuilder()
+ .setDefaultValue(FractionalPercent.newBuilder().setNumerator(50)
+ .setDenominator(DenominatorType.TEN_THOUSAND).build())
+ .build())
+ .setAllowedHeaders(ListStringMatcher.newBuilder()
+ .addPatterns(StringMatcher.newBuilder().setExact("allowed-header").build()).build())
+ .setDisallowedHeaders(ListStringMatcher.newBuilder()
+ .addPatterns(StringMatcher.newBuilder().setPrefix("disallowed-").build()).build())
+ .setDecoderHeaderMutationRules(HeaderMutationRules.newBuilder()
+ .setAllowExpression(RegexMatcher.newBuilder().setRegex("allow.*").build())
+ .setDisallowExpression(RegexMatcher.newBuilder().setRegex("disallow.*").build())
+ .setDisallowAll(BoolValue.of(true)).setDisallowIsError(BoolValue.of(true)).build())
+ .build();
+
+ ExtAuthzConfig config = ExtAuthzConfig.fromProto(extAuthz);
+
+ assertThat(config.grpcService().googleGrpc().target()).isEqualTo("test-cluster");
+ assertThat(config.grpcService().timeout().get().getSeconds()).isEqualTo(5);
+ assertThat(config.grpcService().initialMetadata().isPresent()).isTrue();
+ assertThat(config.failureModeAllow()).isTrue();
+ assertThat(config.failureModeAllowHeaderAdd()).isTrue();
+ assertThat(config.includePeerCertificate()).isTrue();
+ assertThat(config.statusOnError().getCode()).isEqualTo(Status.PERMISSION_DENIED.getCode());
+ assertThat(config.statusOnError().getDescription()).isEqualTo("HTTP status code 403");
+ assertThat(config.denyAtDisable()).isTrue();
+ assertThat(config.filterEnabled()).isEqualTo(Matchers.FractionMatcher.create(50, 10_000));
+ assertThat(config.allowedHeaders()).hasSize(1);
+ assertThat(config.allowedHeaders().get(0).matches("allowed-header")).isTrue();
+ assertThat(config.disallowedHeaders()).hasSize(1);
+ assertThat(config.disallowedHeaders().get(0).matches("disallowed-foo")).isTrue();
+ assertThat(config.decoderHeaderMutationRules().isPresent()).isTrue();
+ HeaderMutationRulesConfig rules = config.decoderHeaderMutationRules().get();
+ assertThat(rules.allowExpression().get().pattern()).isEqualTo("allow.*");
+ assertThat(rules.disallowExpression().get().pattern()).isEqualTo("disallow.*");
+ assertThat(rules.disallowAll()).isTrue();
+ assertThat(rules.disallowIsError()).isTrue();
+ }
+
+ @Test
+ public void fromProto_saneDefaults() throws ExtAuthzParseException {
+ ExtAuthz extAuthz = extAuthzBuilder.build();
+
+ ExtAuthzConfig config = ExtAuthzConfig.fromProto(extAuthz);
+
+ assertThat(config.failureModeAllow()).isFalse();
+ assertThat(config.failureModeAllowHeaderAdd()).isFalse();
+ assertThat(config.includePeerCertificate()).isFalse();
+ assertThat(config.statusOnError()).isEqualTo(Status.PERMISSION_DENIED);
+ assertThat(config.denyAtDisable()).isFalse();
+ assertThat(config.filterEnabled()).isEqualTo(Matchers.FractionMatcher.create(100, 100));
+ assertThat(config.allowedHeaders()).isEmpty();
+ assertThat(config.disallowedHeaders()).isEmpty();
+ assertThat(config.decoderHeaderMutationRules().isPresent()).isFalse();
+ }
+
+ @Test
+ public void fromProto_headerMutationRules_allowExpressionOnly() throws ExtAuthzParseException {
+ ExtAuthz extAuthz = extAuthzBuilder
+ .setDecoderHeaderMutationRules(HeaderMutationRules.newBuilder()
+ .setAllowExpression(RegexMatcher.newBuilder().setRegex("allow.*").build()).build())
+ .build();
+
+ ExtAuthzConfig config = ExtAuthzConfig.fromProto(extAuthz);
+
+ assertThat(config.decoderHeaderMutationRules().isPresent()).isTrue();
+ HeaderMutationRulesConfig rules = config.decoderHeaderMutationRules().get();
+ assertThat(rules.allowExpression().get().pattern()).isEqualTo("allow.*");
+ assertThat(rules.disallowExpression().isPresent()).isFalse();
+ }
+
+ @Test
+ public void fromProto_headerMutationRules_disallowExpressionOnly() throws ExtAuthzParseException {
+ ExtAuthz extAuthz = extAuthzBuilder
+ .setDecoderHeaderMutationRules(HeaderMutationRules.newBuilder()
+ .setDisallowExpression(RegexMatcher.newBuilder().setRegex("disallow.*").build())
+ .build())
+ .build();
+
+ ExtAuthzConfig config = ExtAuthzConfig.fromProto(extAuthz);
+
+ assertThat(config.decoderHeaderMutationRules().isPresent()).isTrue();
+ HeaderMutationRulesConfig rules = config.decoderHeaderMutationRules().get();
+ assertThat(rules.allowExpression().isPresent()).isFalse();
+ assertThat(rules.disallowExpression().get().pattern()).isEqualTo("disallow.*");
+ }
+
+ @Test
+ public void fromProto_filterEnabled_hundred() throws ExtAuthzParseException {
+ ExtAuthz extAuthz = extAuthzBuilder
+ .setFilterEnabled(RuntimeFractionalPercent.newBuilder().setDefaultValue(FractionalPercent
+ .newBuilder().setNumerator(25).setDenominator(DenominatorType.HUNDRED).build()).build())
+ .build();
+
+ ExtAuthzConfig config = ExtAuthzConfig.fromProto(extAuthz);
+
+ assertThat(config.filterEnabled()).isEqualTo(Matchers.FractionMatcher.create(25, 100));
+ }
+
+ @Test
+ public void fromProto_filterEnabled_million() throws ExtAuthzParseException {
+ ExtAuthz extAuthz = extAuthzBuilder
+ .setFilterEnabled(
+ RuntimeFractionalPercent.newBuilder().setDefaultValue(FractionalPercent.newBuilder()
+ .setNumerator(123456).setDenominator(DenominatorType.MILLION).build()).build())
+ .build();
+
+ ExtAuthzConfig config = ExtAuthzConfig.fromProto(extAuthz);
+
+ assertThat(config.filterEnabled())
+ .isEqualTo(Matchers.FractionMatcher.create(123456, 1_000_000));
+ }
+
+ @Test
+ public void fromProto_filterEnabled_unrecognizedDenominator() {
+ ExtAuthz extAuthz = extAuthzBuilder
+ .setFilterEnabled(RuntimeFractionalPercent.newBuilder()
+ .setDefaultValue(
+ FractionalPercent.newBuilder().setNumerator(1).setDenominatorValue(4).build())
+ .build())
+ .build();
+
+ try {
+ ExtAuthzConfig.fromProto(extAuthz);
+ fail("Expected ExtAuthzParseException");
+ } catch (ExtAuthzParseException e) {
+ assertThat(e).hasMessageThat().isEqualTo("Unknown denominator type: UNRECOGNIZED");
+ }
+ }
+}
\ No newline at end of file
diff --git a/xds/src/test/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfigTest.java b/xds/src/test/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfigTest.java
new file mode 100644
index 00000000000..7a506220973
--- /dev/null
+++ b/xds/src/test/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfigTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.grpcservice;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import com.google.common.io.BaseEncoding;
+import com.google.protobuf.Any;
+import com.google.protobuf.Duration;
+import io.envoyproxy.envoy.config.core.v3.GrpcService;
+import io.envoyproxy.envoy.config.core.v3.HeaderValue;
+import io.envoyproxy.envoy.extensions.grpc_service.call_credentials.access_token.v3.AccessTokenCredentials;
+import io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.google_default.v3.GoogleDefaultCredentials;
+import io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.insecure.v3.InsecureCredentials;
+import io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.local.v3.LocalCredentials;
+import io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.xds.v3.XdsCredentials;
+import io.grpc.InsecureChannelCredentials;
+import io.grpc.Metadata;
+import java.nio.charset.StandardCharsets;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class GrpcServiceConfigTest {
+
+ @Test
+ public void fromProto_success() throws GrpcServiceParseException {
+ Any insecureCreds = Any.pack(InsecureCredentials.getDefaultInstance());
+ Any accessTokenCreds =
+ Any.pack(AccessTokenCredentials.newBuilder().setToken("test_token").build());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(insecureCreds).addCallCredentialsPlugin(accessTokenCreds)
+ .build();
+ HeaderValue asciiHeader =
+ HeaderValue.newBuilder().setKey("test_key").setValue("test_value").build();
+ HeaderValue binaryHeader = HeaderValue.newBuilder().setKey("test_key-bin")
+ .setValue(
+ BaseEncoding.base64().encode("test_value_binary".getBytes(StandardCharsets.UTF_8)))
+ .build();
+ Duration timeout = Duration.newBuilder().setSeconds(10).build();
+ GrpcService grpcService =
+ GrpcService.newBuilder().setGoogleGrpc(googleGrpc).addInitialMetadata(asciiHeader)
+ .addInitialMetadata(binaryHeader).setTimeout(timeout).build();
+
+ GrpcServiceConfig config = GrpcServiceConfig.fromProto(grpcService);
+
+ // Assert target URI
+ assertThat(config.googleGrpc().target()).isEqualTo("test_uri");
+
+ // Assert channel credentials
+ assertThat(config.googleGrpc().hashedChannelCredentials().channelCredentials())
+ .isInstanceOf(InsecureChannelCredentials.class);
+ assertThat(config.googleGrpc().hashedChannelCredentials().hash())
+ .isEqualTo(insecureCreds.hashCode());
+
+ // Assert call credentials
+ assertThat(config.googleGrpc().callCredentials().getClass().getName())
+ .isEqualTo("io.grpc.auth.GoogleAuthLibraryCallCredentials");
+
+ // Assert initial metadata
+ assertThat(config.initialMetadata().isPresent()).isTrue();
+ assertThat(config.initialMetadata().get()
+ .get(Metadata.Key.of("test_key", Metadata.ASCII_STRING_MARSHALLER)))
+ .isEqualTo("test_value");
+ assertThat(config.initialMetadata().get()
+ .get(Metadata.Key.of("test_key-bin", Metadata.BINARY_BYTE_MARSHALLER)))
+ .isEqualTo("test_value_binary".getBytes(StandardCharsets.UTF_8));
+
+ // Assert timeout
+ assertThat(config.timeout().isPresent()).isTrue();
+ assertThat(config.timeout().get()).isEqualTo(java.time.Duration.ofSeconds(10));
+ }
+
+ @Test
+ public void fromProto_minimalSuccess_defaults() throws GrpcServiceParseException {
+ Any insecureCreds = Any.pack(InsecureCredentials.getDefaultInstance());
+ Any accessTokenCreds =
+ Any.pack(AccessTokenCredentials.newBuilder().setToken("test_token").build());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(insecureCreds).addCallCredentialsPlugin(accessTokenCreds)
+ .build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+
+ GrpcServiceConfig config = GrpcServiceConfig.fromProto(grpcService);
+
+ assertThat(config.googleGrpc().target()).isEqualTo("test_uri");
+ assertThat(config.initialMetadata().isPresent()).isFalse();
+ assertThat(config.timeout().isPresent()).isFalse();
+ }
+
+ @Test
+ public void fromProto_missingGoogleGrpc() {
+ GrpcService grpcService = GrpcService.newBuilder().build();
+ GrpcServiceParseException exception = assertThrows(GrpcServiceParseException.class,
+ () -> GrpcServiceConfig.fromProto(grpcService));
+ assertThat(exception).hasMessageThat()
+ .startsWith("Unsupported: GrpcService must have GoogleGrpc, got: ");
+ }
+
+ @Test
+ public void fromProto_emptyCallCredentials() {
+ Any insecureCreds = Any.pack(InsecureCredentials.getDefaultInstance());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(insecureCreds).build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+ GrpcServiceParseException exception = assertThrows(GrpcServiceParseException.class,
+ () -> GrpcServiceConfig.fromProto(grpcService));
+ assertThat(exception).hasMessageThat()
+ .isEqualTo("No valid supported call_credentials found. Errors: []");
+ }
+
+ @Test
+ public void fromProto_emptyChannelCredentials() {
+ Any accessTokenCreds =
+ Any.pack(AccessTokenCredentials.newBuilder().setToken("test_token").build());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addCallCredentialsPlugin(accessTokenCreds).build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+ GrpcServiceParseException exception = assertThrows(GrpcServiceParseException.class,
+ () -> GrpcServiceConfig.fromProto(grpcService));
+ assertThat(exception).hasMessageThat()
+ .isEqualTo("No valid supported channel_credentials found. Errors: []");
+ }
+
+ @Test
+ public void fromProto_googleDefaultCredentials() throws GrpcServiceParseException {
+ Any googleDefaultCreds = Any.pack(GoogleDefaultCredentials.getDefaultInstance());
+ Any accessTokenCreds =
+ Any.pack(AccessTokenCredentials.newBuilder().setToken("test_token").build());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(googleDefaultCreds).addCallCredentialsPlugin(accessTokenCreds)
+ .build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+
+ GrpcServiceConfig config = GrpcServiceConfig.fromProto(grpcService);
+
+ assertThat(config.googleGrpc().hashedChannelCredentials().channelCredentials())
+ .isInstanceOf(io.grpc.CompositeChannelCredentials.class);
+ assertThat(config.googleGrpc().hashedChannelCredentials().hash())
+ .isEqualTo(googleDefaultCreds.hashCode());
+ }
+
+ @Test
+ public void fromProto_localCredentials() throws GrpcServiceParseException {
+ Any localCreds = Any.pack(LocalCredentials.getDefaultInstance());
+ Any accessTokenCreds =
+ Any.pack(AccessTokenCredentials.newBuilder().setToken("test_token").build());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(localCreds).addCallCredentialsPlugin(accessTokenCreds).build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+
+ GrpcServiceParseException exception = assertThrows(GrpcServiceParseException.class,
+ () -> GrpcServiceConfig.fromProto(grpcService));
+ assertThat(exception).hasMessageThat().contains("LocalCredentials are not yet supported.");
+ }
+
+ @Test
+ public void fromProto_xdsCredentials_withInsecureFallback() throws GrpcServiceParseException {
+ Any insecureCreds = Any.pack(InsecureCredentials.getDefaultInstance());
+ XdsCredentials xdsCreds =
+ XdsCredentials.newBuilder().setFallbackCredentials(insecureCreds).build();
+ Any xdsCredsAny = Any.pack(xdsCreds);
+ Any accessTokenCreds =
+ Any.pack(AccessTokenCredentials.newBuilder().setToken("test_token").build());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(xdsCredsAny).addCallCredentialsPlugin(accessTokenCreds)
+ .build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+
+ GrpcServiceConfig config = GrpcServiceConfig.fromProto(grpcService);
+
+ assertThat(config.googleGrpc().hashedChannelCredentials().channelCredentials())
+ .isInstanceOf(io.grpc.ChannelCredentials.class);
+ assertThat(config.googleGrpc().hashedChannelCredentials().hash())
+ .isEqualTo(xdsCredsAny.hashCode());
+ }
+
+ @Test
+ public void fromProto_tlsCredentials_notSupported() {
+ Any tlsCreds = Any
+ .pack(io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.tls.v3.TlsCredentials
+ .getDefaultInstance());
+ Any accessTokenCreds =
+ Any.pack(AccessTokenCredentials.newBuilder().setToken("test_token").build());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(tlsCreds).addCallCredentialsPlugin(accessTokenCreds).build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+
+ GrpcServiceParseException exception = assertThrows(GrpcServiceParseException.class,
+ () -> GrpcServiceConfig.fromProto(grpcService));
+ assertThat(exception).hasMessageThat().contains("TlsCredentials are not yet supported.");
+ }
+
+ @Test
+ public void fromProto_invalidChannelCredentialsProto() {
+ // Pack a Duration proto, but try to unpack it as GoogleDefaultCredentials
+ Any invalidCreds = Any.pack(com.google.protobuf.Duration.getDefaultInstance());
+ Any accessTokenCreds =
+ Any.pack(AccessTokenCredentials.newBuilder().setToken("test_token").build());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(invalidCreds).addCallCredentialsPlugin(accessTokenCreds)
+ .build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+
+ GrpcServiceParseException exception = assertThrows(GrpcServiceParseException.class,
+ () -> GrpcServiceConfig.fromProto(grpcService));
+ assertThat(exception).hasMessageThat()
+ .contains("No valid supported channel_credentials found. Errors: [Unsupported channel "
+ + "credentials type: type.googleapis.com/google.protobuf.Duration");
+ }
+
+ @Test
+ public void fromProto_invalidCallCredentialsProto() {
+ // Pack a Duration proto, but try to unpack it as AccessTokenCredentials
+ Any insecureCreds = Any.pack(InsecureCredentials.getDefaultInstance());
+ Any invalidCallCredentials = Any.pack(Duration.getDefaultInstance());
+ GrpcService.GoogleGrpc googleGrpc = GrpcService.GoogleGrpc.newBuilder().setTargetUri("test_uri")
+ .addChannelCredentialsPlugin(insecureCreds).addCallCredentialsPlugin(invalidCallCredentials)
+ .build();
+ GrpcService grpcService = GrpcService.newBuilder().setGoogleGrpc(googleGrpc).build();
+
+ GrpcServiceParseException exception = assertThrows(GrpcServiceParseException.class,
+ () -> GrpcServiceConfig.fromProto(grpcService));
+ assertThat(exception).hasMessageThat().contains("Unsupported call credentials type:");
+ }
+}
+
diff --git a/xds/src/test/java/io/grpc/xds/internal/grpcservice/InsecureGrpcChannelFactoryTest.java b/xds/src/test/java/io/grpc/xds/internal/grpcservice/InsecureGrpcChannelFactoryTest.java
new file mode 100644
index 00000000000..8d7347f56c6
--- /dev/null
+++ b/xds/src/test/java/io/grpc/xds/internal/grpcservice/InsecureGrpcChannelFactoryTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.grpcservice;
+
+import static org.junit.Assert.assertNotNull;
+
+import io.grpc.CallCredentials;
+import io.grpc.InsecureChannelCredentials;
+import io.grpc.ManagedChannel;
+import io.grpc.Metadata;
+import io.grpc.xds.internal.grpcservice.GrpcServiceConfig.GoogleGrpcConfig;
+import io.grpc.xds.internal.grpcservice.GrpcServiceConfig.HashedChannelCredentials;
+import java.util.concurrent.Executor;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link InsecureGrpcChannelFactory}. */
+@RunWith(JUnit4.class)
+public class InsecureGrpcChannelFactoryTest {
+
+ private static final class NoOpCallCredentials extends CallCredentials {
+ @Override
+ public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor,
+ MetadataApplier applier) {
+ applier.apply(new Metadata());
+ }
+ }
+
+ @Test
+ public void testCreateChannel() {
+ InsecureGrpcChannelFactory factory = InsecureGrpcChannelFactory.getInstance();
+ GrpcServiceConfig config = GrpcServiceConfig.builder()
+ .googleGrpc(GoogleGrpcConfig.builder().target("localhost:8080")
+ .hashedChannelCredentials(
+ HashedChannelCredentials.of(InsecureChannelCredentials.create(), 0))
+ .callCredentials(new NoOpCallCredentials()).build())
+ .build();
+ ManagedChannel channel = factory.createChannel(config);
+ assertNotNull(channel);
+ channel.shutdownNow();
+ }
+}
diff --git a/xds/src/test/java/io/grpc/xds/internal/headermutations/HeaderMutationRulesConfigTest.java b/xds/src/test/java/io/grpc/xds/internal/headermutations/HeaderMutationRulesConfigTest.java
new file mode 100644
index 00000000000..e2bda9cb836
--- /dev/null
+++ b/xds/src/test/java/io/grpc/xds/internal/headermutations/HeaderMutationRulesConfigTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2025 The 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 io.grpc.xds.internal.headermutations;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.regex.Pattern;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class HeaderMutationRulesConfigTest {
+ @Test
+ public void testBuilderDefaultValues() {
+ HeaderMutationRulesConfig config = HeaderMutationRulesConfig.builder().build();
+ assertFalse(config.disallowAll());
+ assertFalse(config.disallowIsError());
+ assertThat(config.allowExpression()).isEmpty();
+ assertThat(config.disallowExpression()).isEmpty();
+ }
+
+ @Test
+ public void testBuilder_setDisallowAll() {
+ HeaderMutationRulesConfig config =
+ HeaderMutationRulesConfig.builder().disallowAll(true).build();
+ assertTrue(config.disallowAll());
+ }
+
+ @Test
+ public void testBuilder_setDisallowIsError() {
+ HeaderMutationRulesConfig config =
+ HeaderMutationRulesConfig.builder().disallowIsError(true).build();
+ assertTrue(config.disallowIsError());
+ }
+
+ @Test
+ public void testBuilder_setAllowExpression() {
+ Pattern pattern = Pattern.compile("allow.*");
+ HeaderMutationRulesConfig config =
+ HeaderMutationRulesConfig.builder().allowExpression(pattern).build();
+ assertThat(config.allowExpression()).hasValue(pattern);
+ }
+
+ @Test
+ public void testBuilder_setDisallowExpression() {
+ Pattern pattern = Pattern.compile("disallow.*");
+ HeaderMutationRulesConfig config =
+ HeaderMutationRulesConfig.builder().disallowExpression(pattern).build();
+ assertThat(config.disallowExpression()).hasValue(pattern);
+ }
+
+ @Test
+ public void testBuilder_setAll() {
+ Pattern allowPattern = Pattern.compile("allow.*");
+ Pattern disallowPattern = Pattern.compile("disallow.*");
+ HeaderMutationRulesConfig config = HeaderMutationRulesConfig.builder()
+ .disallowAll(true)
+ .disallowIsError(true)
+ .allowExpression(allowPattern)
+ .disallowExpression(disallowPattern)
+ .build();
+ assertTrue(config.disallowAll());
+ assertTrue(config.disallowIsError());
+ assertThat(config.allowExpression()).hasValue(allowPattern);
+ assertThat(config.disallowExpression()).hasValue(disallowPattern);
+ }
+}
diff --git a/xds/third_party/envoy/import.sh b/xds/third_party/envoy/import.sh
index ba657612586..2fed6ae47dd 100755
--- a/xds/third_party/envoy/import.sh
+++ b/xds/third_party/envoy/import.sh
@@ -16,8 +16,8 @@
# Update VERSION then execute this script
set -e
-# import VERSION from the google internal copybara_version.txt for Envoy
-VERSION=1128a52d227efb8c798478d293fdc05e8075ebcd
+# import VERSION from internal go/envoy-import-status
+VERSION=b6df993feef0340391e6dbf6ad957ab42884ad05
DOWNLOAD_URL="https://github.com/envoyproxy/envoy/archive/${VERSION}.tar.gz"
DOWNLOAD_BASE_DIR="envoy-${VERSION}"
SOURCE_PROTO_BASE_DIR="${DOWNLOAD_BASE_DIR}/api"
@@ -33,6 +33,7 @@ envoy/config/cluster/v3/circuit_breaker.proto
envoy/config/cluster/v3/cluster.proto
envoy/config/cluster/v3/filter.proto
envoy/config/cluster/v3/outlier_detection.proto
+envoy/config/common/mutation_rules/v3/mutation_rules.proto
envoy/config/core/v3/address.proto
envoy/config/core/v3/backoff.proto
envoy/config/core/v3/base.proto
@@ -74,12 +75,19 @@ envoy/config/trace/v3/zipkin.proto
envoy/data/accesslog/v3/accesslog.proto
envoy/extensions/clusters/aggregate/v3/cluster.proto
envoy/extensions/filters/common/fault/v3/fault.proto
+envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto
envoy/extensions/filters/http/fault/v3/fault.proto
envoy/extensions/filters/http/rate_limit_quota/v3/rate_limit_quota.proto
envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.proto
envoy/extensions/filters/http/rbac/v3/rbac.proto
envoy/extensions/filters/http/router/v3/router.proto
envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto
+envoy/extensions/grpc_service/call_credentials/access_token/v3/access_token_credentials.proto
+envoy/extensions/grpc_service/channel_credentials/google_default/v3/google_default_credentials.proto
+envoy/extensions/grpc_service/channel_credentials/insecure/v3/insecure_credentials.proto
+envoy/extensions/grpc_service/channel_credentials/local/v3/local_credentials.proto
+envoy/extensions/grpc_service/channel_credentials/tls/v3/tls_credentials.proto
+envoy/extensions/grpc_service/channel_credentials/xds/v3/xds_credentials.proto
envoy/extensions/load_balancing_policies/client_side_weighted_round_robin/v3/client_side_weighted_round_robin.proto
envoy/extensions/load_balancing_policies/common/v3/common.proto
envoy/extensions/load_balancing_policies/least_request/v3/least_request.proto
@@ -92,6 +100,8 @@ envoy/extensions/transport_sockets/tls/v3/cert.proto
envoy/extensions/transport_sockets/tls/v3/common.proto
envoy/extensions/transport_sockets/tls/v3/secret.proto
envoy/extensions/transport_sockets/tls/v3/tls.proto
+envoy/service/auth/v3/attribute_context.proto
+envoy/service/auth/v3/external_auth.proto
envoy/service/discovery/v3/ads.proto
envoy/service/discovery/v3/discovery.proto
envoy/service/load_stats/v3/lrs.proto
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/bootstrap/v3/bootstrap.proto b/xds/third_party/envoy/src/main/proto/envoy/config/bootstrap/v3/bootstrap.proto
index bf65f3df45c..28b1eba6680 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/bootstrap/v3/bootstrap.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/bootstrap/v3/bootstrap.proto
@@ -41,7 +41,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// ` for more detail.
// Bootstrap :ref:`configuration overview `.
-// [#next-free-field: 42]
+// [#next-free-field: 43]
message Bootstrap {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.bootstrap.v2.Bootstrap";
@@ -230,6 +230,14 @@ message Bootstrap {
bool stats_flush_on_admin = 29 [(validate.rules).bool = {const: true}];
}
+ oneof stats_eviction {
+ // Optional duration to perform metric eviction. At every interval, during the stats flush
+ // the unused metrics are removed from the worker caches and the used metrics
+ // are marked as unused. Must be a multiple of the ``stats_flush_interval``.
+ google.protobuf.Duration stats_eviction_interval = 42
+ [(validate.rules).duration = {gte {nanos: 1000000}}];
+ }
+
// Optional watchdog configuration.
// This is for a single watchdog configuration for the entire system.
// Deprecated in favor of ``watchdogs`` which has finer granularity.
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/cluster/v3/cluster.proto b/xds/third_party/envoy/src/main/proto/envoy/config/cluster/v3/cluster.proto
index 51180b1e855..c5112458a71 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/cluster/v3/cluster.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/cluster/v3/cluster.proto
@@ -652,9 +652,10 @@ message Cluster {
// If this is not set, we default to a merge window of 1000ms. To disable it, set the merge
// window to 0.
//
- // Note: merging does not apply to cluster membership changes (e.g.: adds/removes); this is
- // because merging those updates isn't currently safe. See
- // https://github.com/envoyproxy/envoy/pull/3941.
+ // .. note::
+ // Merging does not apply to cluster membership changes (e.g.: adds/removes); this is
+ // because merging those updates isn't currently safe. See
+ // https://github.com/envoyproxy/envoy/pull/3941.
google.protobuf.Duration update_merge_window = 4;
// If set to true, Envoy will :ref:`exclude ` new hosts
@@ -816,12 +817,14 @@ message Cluster {
string name = 1 [(validate.rules).string = {min_len: 1}];
// An optional alternative to the cluster name to be used for observability. This name is used
- // emitting stats for the cluster and access logging the cluster name. This will appear as
+ // for emitting stats for the cluster and access logging the cluster name. This will appear as
// additional information in configuration dumps of a cluster's current status as
// :ref:`observability_name `
- // and as an additional tag "upstream_cluster.name" while tracing. Note: Any ``:`` in the name
- // will be converted to ``_`` when emitting statistics. This should not be confused with
- // :ref:`Router Filter Header `.
+ // and as an additional tag "upstream_cluster.name" while tracing.
+ //
+ // .. note::
+ // Any ``:`` in the name will be converted to ``_`` when emitting statistics. This should not be confused with
+ // :ref:`Router Filter Header `.
string alt_stat_name = 28 [(udpa.annotations.field_migrate).rename = "observability_name"];
oneof cluster_discovery_type {
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/common/mutation_rules/v3/mutation_rules.proto b/xds/third_party/envoy/src/main/proto/envoy/config/common/mutation_rules/v3/mutation_rules.proto
new file mode 100644
index 00000000000..c015db21431
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/common/mutation_rules/v3/mutation_rules.proto
@@ -0,0 +1,113 @@
+syntax = "proto3";
+
+package envoy.config.common.mutation_rules.v3;
+
+import "envoy/config/core/v3/base.proto";
+import "envoy/type/matcher/v3/regex.proto";
+import "envoy/type/matcher/v3/string.proto";
+
+import "google/protobuf/wrappers.proto";
+
+import "udpa/annotations/status.proto";
+import "validate/validate.proto";
+
+option java_package = "io.envoyproxy.envoy.config.common.mutation_rules.v3";
+option java_outer_classname = "MutationRulesProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/common/mutation_rules/v3;mutation_rulesv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: Header mutation rules]
+
+// The HeaderMutationRules structure specifies what headers may be
+// manipulated by a processing filter. This set of rules makes it
+// possible to control which modifications a filter may make.
+//
+// By default, an external processing server may add, modify, or remove
+// any header except for an "Envoy internal" header (which is typically
+// denoted by an x-envoy prefix) or specific headers that may affect
+// further filter processing:
+//
+// * ``host``
+// * ``:authority``
+// * ``:scheme``
+// * ``:method``
+//
+// Every attempt to add, change, append, or remove a header will be
+// tested against the rules here. Disallowed header mutations will be
+// ignored unless ``disallow_is_error`` is set to true.
+//
+// Attempts to remove headers are further constrained -- regardless of the
+// settings, system-defined headers (that start with ``:``) and the ``host``
+// header may never be removed.
+//
+// In addition, a counter will be incremented whenever a mutation is
+// rejected. In the ext_proc filter, that counter is named
+// ``rejected_header_mutations``.
+// [#next-free-field: 8]
+message HeaderMutationRules {
+ // By default, certain headers that could affect processing of subsequent
+ // filters or request routing cannot be modified. These headers are
+ // ``host``, ``:authority``, ``:scheme``, and ``:method``. Setting this parameter
+ // to true allows these headers to be modified as well.
+ google.protobuf.BoolValue allow_all_routing = 1;
+
+ // If true, allow modification of envoy internal headers. By default, these
+ // start with ``x-envoy`` but this may be overridden in the ``Bootstrap``
+ // configuration using the
+ // :ref:`header_prefix `
+ // field. Default is false.
+ google.protobuf.BoolValue allow_envoy = 2;
+
+ // If true, prevent modification of any system header, defined as a header
+ // that starts with a ``:`` character, regardless of any other settings.
+ // A processing server may still override the ``:status`` of an HTTP response
+ // using an ``ImmediateResponse`` message. Default is false.
+ google.protobuf.BoolValue disallow_system = 3;
+
+ // If true, prevent modifications of all header values, regardless of any
+ // other settings. A processing server may still override the ``:status``
+ // of an HTTP response using an ``ImmediateResponse`` message. Default is false.
+ google.protobuf.BoolValue disallow_all = 4;
+
+ // If set, specifically allow any header that matches this regular
+ // expression. This overrides all other settings except for
+ // ``disallow_expression``.
+ type.matcher.v3.RegexMatcher allow_expression = 5;
+
+ // If set, specifically disallow any header that matches this regular
+ // expression regardless of any other settings.
+ type.matcher.v3.RegexMatcher disallow_expression = 6;
+
+ // If true, and if the rules in this list cause a header mutation to be
+ // disallowed, then the filter using this configuration will terminate the
+ // request with a 500 error. In addition, regardless of the setting of this
+ // parameter, any attempt to set, add, or modify a disallowed header will
+ // cause the ``rejected_header_mutations`` counter to be incremented.
+ // Default is false.
+ google.protobuf.BoolValue disallow_is_error = 7;
+}
+
+// The HeaderMutation structure specifies an action that may be taken on HTTP
+// headers.
+message HeaderMutation {
+ message RemoveOnMatch {
+ // A string matcher that will be applied to the header key. If the header key
+ // matches, the header will be removed.
+ type.matcher.v3.StringMatcher key_matcher = 1 [(validate.rules).message = {required: true}];
+ }
+
+ oneof action {
+ option (validate.required) = true;
+
+ // Remove the specified header if it exists.
+ string remove = 1
+ [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}];
+
+ // Append new header by the specified HeaderValueOption.
+ core.v3.HeaderValueOption append = 2;
+
+ // Remove the header if the key matches the specified string matcher.
+ RemoveOnMatch remove_on_match = 3;
+ }
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/address.proto b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/address.proto
index 56796fc721a..238494a09c7 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/address.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/address.proto
@@ -105,9 +105,6 @@ message SocketAddress {
// .. note::
// Setting this parameter requires Envoy to run with the ``CAP_NET_ADMIN`` capability.
//
- // .. note::
- // Currently only used for Listener sockets.
- //
// .. attention::
// Network namespaces are only configurable on Linux. Otherwise, this field has no effect.
string network_namespace_filepath = 7;
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/config_source.proto b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/config_source.proto
index f0effd99e45..430562aa5bd 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/config_source.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/config_source.proto
@@ -276,7 +276,8 @@ message ExtensionConfigSource {
// to be supplied.
bool apply_default_config_without_warming = 3;
- // A set of permitted extension type URLs. Extension configuration updates are rejected
- // if they do not match any type URL in the set.
+ // A set of permitted extension type URLs for the type encoded inside of the
+ // :ref:`TypedExtensionConfig `. Extension
+ // configuration updates are rejected if they do not match any type URL in the set.
repeated string type_urls = 4 [(validate.rules).repeated = {min_items: 1}];
}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/grpc_service.proto b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/grpc_service.proto
index 5fd7921a806..f8feb2f516f 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/grpc_service.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/grpc_service.proto
@@ -64,7 +64,7 @@ message GrpcService {
bool skip_envoy_headers = 5;
}
- // [#next-free-field: 9]
+ // [#next-free-field: 11]
message GoogleGrpc {
option (udpa.annotations.versioning).previous_message_type =
"envoy.api.v2.core.GrpcService.GoogleGrpc";
@@ -249,16 +249,31 @@ message GrpcService {
}
// The target URI when using the `Google C++ gRPC client
- // `_. SSL credentials will be supplied in
- // :ref:`channel_credentials `.
+ // `_.
string target_uri = 1 [(validate.rules).string = {min_len: 1}];
+ // The channel credentials to use. See `channel credentials
+ // `_.
+ // Ignored if ``channel_credentials_plugin`` is set.
ChannelCredentials channel_credentials = 2;
- // A set of call credentials that can be composed with `channel credentials
+ // A list of channel credentials plugins.
+ // The data plane will iterate over the list in order and stop at the first credential type
+ // that it supports. This provides a mechanism for starting to use new credential types that
+ // are not yet supported by all data planes.
+ // [#not-implemented-hide:]
+ repeated google.protobuf.Any channel_credentials_plugin = 9;
+
+ // The call credentials to use. See `channel credentials
// `_.
+ // Ignored if ``call_credentials_plugin`` is set.
repeated CallCredentials call_credentials = 3;
+ // A list of call credentials plugins. All supported plugins will be used.
+ // Unsupported plugin types will be ignored.
+ // [#not-implemented-hide:]
+ repeated google.protobuf.Any call_credentials_plugin = 10;
+
// The human readable prefix to use when emitting statistics for the gRPC
// service.
//
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/health_check.proto b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/health_check.proto
index fd4440d8fa5..a4ed6e91818 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/health_check.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/health_check.proto
@@ -102,7 +102,8 @@ message HealthCheck {
// ``/healthcheck``.
string path = 2 [(validate.rules).string = {min_len: 1 well_known_regex: HTTP_HEADER_VALUE}];
- // [#not-implemented-hide:] HTTP specific payload.
+ // HTTP specific payload to be sent as the request body during health checking.
+ // If specified, the method should support a request body (POST, PUT, PATCH, etc.).
Payload send = 3;
// Specifies a list of HTTP expected responses to match in the first ``response_buffer_size`` bytes of the response body.
@@ -161,7 +162,8 @@ message HealthCheck {
type.matcher.v3.StringMatcher service_name_matcher = 11;
// HTTP Method that will be used for health checking, default is "GET".
- // GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, PATCH methods are supported, but making request body is not supported.
+ // GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, PATCH methods are supported.
+ // Request body payloads are supported for POST, PUT, PATCH, and OPTIONS methods only.
// CONNECT method is disallowed because it is not appropriate for health check request.
// If a non-200 response is expected by the method, it needs to be set in :ref:`expected_statuses `.
RequestMethod method = 13 [(validate.rules).enum = {defined_only: true not_in: 6}];
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/protocol.proto b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/protocol.proto
index edab4cd79c6..74fe641fe3a 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/protocol.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/core/v3/protocol.proto
@@ -77,7 +77,7 @@ message QuicProtocolOptions {
[(validate.rules).uint32 = {lte: 16777216 gte: 1}];
// Similar to ``initial_stream_window_size``, but for connection-level
- // flow-control. Valid values rage from 1 to 25165824 (24MB, maximum supported by QUICHE) and defaults
+ // flow-control. Valid values range from 1 to 25165824 (24MB, maximum supported by QUICHE) and defaults
// to 25165824 (24 * 1024 * 1024).
//
// .. note::
@@ -111,10 +111,9 @@ message QuicProtocolOptions {
// default 600s will be applied.
// For internal corporate network, a long timeout is often fine.
// But for client facing network, 30s is usually a good choice.
- google.protobuf.Duration idle_network_timeout = 8 [(validate.rules).duration = {
- lte {seconds: 600}
- gte {seconds: 1}
- }];
+ // Do not add an upper bound here. A long idle timeout is useful for maintaining warm connections at non-front-line proxy for low QPS services."
+ google.protobuf.Duration idle_network_timeout = 8
+ [(validate.rules).duration = {gte {seconds: 1}}];
// Maximum packet length for QUIC connections. It refers to the largest size of a QUIC packet that can be transmitted over the connection.
// If not specified, one of the `default values in QUICHE `_ is used.
@@ -276,7 +275,7 @@ message HttpProtocolOptions {
// The default value for responses can be overridden by setting runtime key ``envoy.reloadable_features.max_response_headers_count``.
// Downstream requests that exceed this limit will receive a 431 response for HTTP/1.x and cause a stream
// reset for HTTP/2.
- // Upstream responses that exceed this limit will result in a 503 response.
+ // Upstream responses that exceed this limit will result in a 502 response.
google.protobuf.UInt32Value max_headers_count = 2 [(validate.rules).uint32 = {gte: 1}];
// The maximum size of response headers.
@@ -420,7 +419,7 @@ message Http1ProtocolOptions {
// envoy.reloadable_features.http1_use_balsa_parser.
// See issue #21245.
google.protobuf.BoolValue use_balsa_parser = 9
- [(xds.annotations.v3.field_status).work_in_progress = true];
+ [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];
// [#not-implemented-hide:] Hiding so that field can be removed.
// If true, and BalsaParser is used (either `use_balsa_parser` above is true,
@@ -503,7 +502,7 @@ message Http2ProtocolOptions {
// `Maximum concurrent streams `_
// allowed for peer on one HTTP/2 connection. Valid values range from 1 to 2147483647 (2^31 - 1)
- // and defaults to 2147483647.
+ // and defaults to 1024 for safety and should be sufficient for most use cases.
//
// For upstream connections, this also limits how many streams Envoy will initiate concurrently
// on a single connection. If the limit is reached, Envoy may queue requests or establish
@@ -517,8 +516,8 @@ message Http2ProtocolOptions {
// `Initial stream-level flow-control window
// `_ size. Valid values range from 65535
- // (2^16 - 1, HTTP/2 default) to 2147483647 (2^31 - 1, HTTP/2 maximum) and defaults to 268435456
- // (256 * 1024 * 1024).
+ // (2^16 - 1, HTTP/2 default) to 2147483647 (2^31 - 1, HTTP/2 maximum) and defaults to
+ // 16MiB (16 * 1024 * 1024).
//
// .. note::
//
@@ -532,7 +531,7 @@ message Http2ProtocolOptions {
[(validate.rules).uint32 = {lte: 2147483647 gte: 65535}];
// Similar to ``initial_stream_window_size``, but for connection-level flow-control
- // window. Currently, this has the same minimum/maximum/default as ``initial_stream_window_size``.
+ // window. The default is 24MiB (24 * 1024 * 1024).
google.protobuf.UInt32Value initial_connection_window_size = 4
[(validate.rules).uint32 = {lte: 2147483647 gte: 65535}];
@@ -674,7 +673,7 @@ message GrpcProtocolOptions {
}
// A message which allows using HTTP/3.
-// [#next-free-field: 8]
+// [#next-free-field: 9]
message Http3ProtocolOptions {
QuicProtocolOptions quic_protocol_options = 1;
@@ -709,6 +708,10 @@ message Http3ProtocolOptions {
// No huffman encoding, zero dynamic table capacity and no cookie crumbing.
// This can be useful for trading off CPU vs bandwidth when an upstream HTTP/3 connection multiplexes multiple downstream connections.
bool disable_qpack = 7;
+
+ // Disables connection level flow control for HTTP/3 streams. This is useful in situations where the streams share the same connection
+ // but originate from different end-clients, so that each stream can make progress independently at non-front-line proxies.
+ bool disable_connection_flow_control_for_streams = 8;
}
// A message to control transformations to the :scheme header
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/endpoint/v3/endpoint.proto b/xds/third_party/envoy/src/main/proto/envoy/config/endpoint/v3/endpoint.proto
index 894f68310a4..a149f6095c1 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/endpoint/v3/endpoint.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/endpoint/v3/endpoint.proto
@@ -113,8 +113,9 @@ message ClusterLoadAssignment {
// to determine the health of the priority level, or in other words assume each host has a weight of 1 for
// this calculation.
//
- // Note: this is not currently implemented for
- // :ref:`locality weighted load balancing `.
+ // .. note::
+ // This is not currently implemented for
+ // :ref:`locality weighted load balancing `.
bool weighted_priority_health = 6;
}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/endpoint/v3/load_report.proto b/xds/third_party/envoy/src/main/proto/envoy/config/endpoint/v3/load_report.proto
index 32bbfe2d3f6..6d12765cef5 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/endpoint/v3/load_report.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/endpoint/v3/load_report.proto
@@ -38,7 +38,8 @@ message UpstreamLocalityStats {
// locality.
uint64 total_successful_requests = 2;
- // The total number of unfinished requests
+ // The total number of unfinished requests. A request can be an HTTP request
+ // or a TCP connection for a TCP connection pool.
uint64 total_requests_in_progress = 3;
// The total number of requests that failed due to errors at the endpoint,
@@ -47,7 +48,8 @@ message UpstreamLocalityStats {
// The total number of requests that were issued by this Envoy since
// the last report. This information is aggregated over all the
- // upstream endpoints in the locality.
+ // upstream endpoints in the locality. A request can be an HTTP request
+ // or a TCP connection for a TCP connection pool.
uint64 total_issued_requests = 8;
// The total number of connections in an established state at the time of the
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/listener/v3/listener_components.proto b/xds/third_party/envoy/src/main/proto/envoy/config/listener/v3/listener_components.proto
index 33eb349fd06..cfa30afbb68 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/listener/v3/listener_components.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/listener/v3/listener_components.proto
@@ -233,7 +233,7 @@ message FilterChain {
google.protobuf.BoolValue use_proxy_proto = 4
[deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];
- // [#not-implemented-hide:] filter chain metadata.
+ // Filter chain metadata.
core.v3.Metadata metadata = 5;
// Optional custom transport socket implementation to use for downstream connections.
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/metrics/v3/stats.proto b/xds/third_party/envoy/src/main/proto/envoy/config/metrics/v3/stats.proto
index e7d7f80d648..02bb23aec9d 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/metrics/v3/stats.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/metrics/v3/stats.proto
@@ -298,10 +298,12 @@ message HistogramBucketSettings {
// Each value is the upper bound of a bucket. Each bucket must be greater than 0 and unique.
// The order of the buckets does not matter.
repeated double buckets = 2 [(validate.rules).repeated = {
- min_items: 1
unique: true
items {double {gt: 0.0}}
}];
+
+ // Initial number of bins for the ``circllhist`` thread local histogram per time series. Default value is 100.
+ google.protobuf.UInt32Value bins = 3 [(validate.rules).uint32 = {lte: 46082 gt: 0}];
}
// Stats configuration proto schema for built-in ``envoy.stat_sinks.statsd`` sink. This sink does not support
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/overload/v3/overload.proto b/xds/third_party/envoy/src/main/proto/envoy/config/overload/v3/overload.proto
index 1f267c1863d..b5bc2c4d830 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/overload/v3/overload.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/overload/v3/overload.proto
@@ -109,6 +109,13 @@ message ScaleTimersOverloadActionConfig {
// :ref:`HttpConnectionManager.common_http_protocol_options.max_connection_duration
// `.
HTTP_DOWNSTREAM_CONNECTION_MAX = 4;
+
+ // Adjusts the timeout for the downstream codec to flush an ended stream.
+ // This affects the value of :ref:`RouteAction.flush_timeout
+ // ` and
+ // :ref:`HttpConnectionManager.stream_flush_timeout
+ // `
+ HTTP_DOWNSTREAM_STREAM_FLUSH = 5;
}
message ScaleTimer {
@@ -134,9 +141,16 @@ message OverloadAction {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.overload.v2alpha.OverloadAction";
- // The name of the overload action. This is just a well-known string that listeners can
- // use for registering callbacks. Custom overload actions should be named using reverse
- // DNS to ensure uniqueness.
+ // The name of the overload action. This is just a well-known string that
+ // listeners can use for registering callbacks.
+ // Valid known overload actions include:
+ // - envoy.overload_actions.stop_accepting_requests
+ // - envoy.overload_actions.disable_http_keepalive
+ // - envoy.overload_actions.stop_accepting_connections
+ // - envoy.overload_actions.reject_incoming_connections
+ // - envoy.overload_actions.shrink_heap
+ // - envoy.overload_actions.reduce_timeouts
+ // - envoy.overload_actions.reset_high_memory_stream
string name = 1 [(validate.rules).string = {min_len: 1}];
// A set of triggers for this action. The state of the action is the maximum
@@ -148,7 +162,7 @@ message OverloadAction {
// in this list.
repeated Trigger triggers = 2 [(validate.rules).repeated = {min_items: 1}];
- // Configuration for the action being instantiated.
+ // Configuration for the action being instantiated if applicable.
google.protobuf.Any typed_config = 3;
}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/route/v3/route_components.proto b/xds/third_party/envoy/src/main/proto/envoy/config/route/v3/route_components.proto
index 292e5b93558..6837ade69d8 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/route/v3/route_components.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/route/v3/route_components.proto
@@ -41,7 +41,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// host header. This allows a single listener to service multiple top level domain path trees. Once
// a virtual host is selected based on the domain, the routes are processed in order to see which
// upstream cluster to route to or whether to perform a redirect.
-// [#next-free-field: 25]
+// [#next-free-field: 26]
message VirtualHost {
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.VirtualHost";
@@ -205,10 +205,37 @@ message VirtualHost {
// request header in retries initiated by per try timeouts.
bool include_is_timeout_retry_header = 23;
- // The maximum bytes which will be buffered for retries and shadowing.
- // If set and a route-specific limit is not set, the bytes actually buffered will be the minimum
- // value of this and the listener per_connection_buffer_limit_bytes.
- google.protobuf.UInt32Value per_request_buffer_limit_bytes = 18;
+ // The maximum bytes which will be buffered for retries and shadowing. If set, the bytes actually buffered will be
+ // the minimum value of this and the listener ``per_connection_buffer_limit_bytes``.
+ //
+ // .. attention::
+ //
+ // This field has been deprecated. Please use :ref:`request_body_buffer_limit
+ // ` instead.
+ // Only one of ``per_request_buffer_limit_bytes`` and ``request_body_buffer_limit`` could be set.
+ google.protobuf.UInt32Value per_request_buffer_limit_bytes = 18
+ [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];
+
+ // The maximum bytes which will be buffered for request bodies to support large request body
+ // buffering beyond the ``per_connection_buffer_limit_bytes``.
+ //
+ // This limit is specifically for the request body buffering and allows buffering larger payloads while maintaining
+ // flow control.
+ //
+ // Buffer limit precedence (from highest to lowest priority):
+ //
+ // 1. If ``request_body_buffer_limit`` is set, then ``request_body_buffer_limit`` will be used.
+ // 2. If :ref:`per_request_buffer_limit_bytes `
+ // is set but ``request_body_buffer_limit`` is not, then ``min(per_request_buffer_limit_bytes, per_connection_buffer_limit_bytes)``
+ // will be used.
+ // 3. If neither is set, then ``per_connection_buffer_limit_bytes`` will be used.
+ //
+ // For flow control chunk sizes, ``min(per_connection_buffer_limit_bytes, 16KB)`` will be used.
+ //
+ // Only one of :ref:`per_request_buffer_limit_bytes `
+ // and ``request_body_buffer_limit`` could be set.
+ google.protobuf.UInt64Value request_body_buffer_limit = 25
+ [(validate.rules).message = {required: false}];
// Specify a set of default request mirroring policies for every route under this virtual host.
// It takes precedence over the route config mirror policy entirely.
@@ -244,7 +271,7 @@ message RouteList {
//
// Envoy supports routing on HTTP method via :ref:`header matching
// `.
-// [#next-free-field: 20]
+// [#next-free-field: 21]
message Route {
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.Route";
@@ -341,7 +368,14 @@ message Route {
// The maximum bytes which will be buffered for retries and shadowing.
// If set, the bytes actually buffered will be the minimum value of this and the
// listener per_connection_buffer_limit_bytes.
- google.protobuf.UInt32Value per_request_buffer_limit_bytes = 16;
+ //
+ // .. attention::
+ //
+ // This field has been deprecated. Please use :ref:`request_body_buffer_limit
+ // ` instead.
+ // Only one of ``per_request_buffer_limit_bytes`` and ``request_body_buffer_limit`` may be set.
+ google.protobuf.UInt32Value per_request_buffer_limit_bytes = 16
+ [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];
// The human readable prefix to use when emitting statistics for this endpoint.
// The statistics are rooted at vhost..route..
@@ -357,6 +391,25 @@ message Route {
// every application endpoint. This is both not easily maintainable and
// statistics use a non-trivial amount of memory(approximately 1KiB per route).
string stat_prefix = 19;
+
+ // The maximum bytes which will be buffered for request bodies to support large request body
+ // buffering beyond the ``per_connection_buffer_limit_bytes``.
+ //
+ // This limit is specifically for the request body buffering and allows buffering larger payloads while maintaining
+ // flow control.
+ //
+ // Buffer limit precedence (from highest to lowest priority):
+ //
+ // 1. If ``request_body_buffer_limit`` is set: use ``request_body_buffer_limit``
+ // 2. If :ref:`per_request_buffer_limit_bytes `
+ // is set but ``request_body_buffer_limit`` is not: use ``min(per_request_buffer_limit_bytes, per_connection_buffer_limit_bytes)``
+ // 3. If neither is set: use ``per_connection_buffer_limit_bytes``
+ //
+ // For flow control chunk sizes, use ``min(per_connection_buffer_limit_bytes, 16KB)``.
+ //
+ // Only one of :ref:`per_request_buffer_limit_bytes `
+ // and ``request_body_buffer_limit`` may be set.
+ google.protobuf.UInt64Value request_body_buffer_limit = 20;
}
// Compared to the :ref:`cluster ` field that specifies a
@@ -365,6 +418,7 @@ message Route {
// multiple upstream clusters along with weights that indicate the percentage of
// traffic to be forwarded to each cluster. The router selects an upstream cluster based on the
// weights.
+// [#next-free-field: 6]
message WeightedCluster {
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.WeightedCluster";
@@ -495,6 +549,10 @@ message WeightedCluster {
// the process for the consistency. And the value is a unsigned number between 0 and UINT64_MAX.
string header_name = 4
[(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME strict: false}];
+
+ // When set to true, the hash policies will be used to generate the random value for weighted cluster selection.
+ // This could ensure consistent cluster picking across multiple proxy levels for weighted traffic.
+ google.protobuf.BoolValue use_hash_policy = 5;
}
}
@@ -740,7 +798,7 @@ message CorsPolicy {
google.protobuf.BoolValue forward_not_matching_preflights = 13;
}
-// [#next-free-field: 42]
+// [#next-free-field: 43]
message RouteAction {
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RouteAction";
@@ -1265,8 +1323,28 @@ message RouteAction {
// If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts"
// is configured, this timeout is scaled according to the value for
// :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `.
+ //
+ // This timeout may also be used in place of ``flush_timeout`` in very specific cases. See the
+ // documentation for ``flush_timeout`` for more details.
google.protobuf.Duration idle_timeout = 24;
+ // Specifies the codec stream flush timeout for the route.
+ //
+ // If not specified, the first preference is the global :ref:`stream_flush_timeout
+ // `,
+ // but only if explicitly configured.
+ //
+ // If neither the explicit HCM-wide flush timeout nor this route-specific flush timeout is configured,
+ // the route's stream idle timeout is reused for this timeout. This is for
+ // backwards compatibility since both behaviors were historically controlled by the one timeout.
+ //
+ // If the route also does not have an idle timeout configured, the global :ref:`stream_idle_timeout
+ // `. used, again
+ // for backwards compatibility. That timeout defaults to 5 minutes.
+ //
+ // A value of 0 via any of the above paths will completely disable the timeout for a given route.
+ google.protobuf.Duration flush_timeout = 42;
+
// Specifies how to send request over TLS early data.
// If absent, allows `safe HTTP requests `_ to be sent on early data.
// [#extension-category: envoy.route.early_data_policy]
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/trace/v3/opentelemetry.proto b/xds/third_party/envoy/src/main/proto/envoy/config/trace/v3/opentelemetry.proto
index 59028326f22..5260d9bd6af 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/trace/v3/opentelemetry.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/trace/v3/opentelemetry.proto
@@ -6,6 +6,8 @@ import "envoy/config/core/v3/extension.proto";
import "envoy/config/core/v3/grpc_service.proto";
import "envoy/config/core/v3/http_service.proto";
+import "google/protobuf/wrappers.proto";
+
import "udpa/annotations/migrate.proto";
import "udpa/annotations/status.proto";
@@ -19,7 +21,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// Configuration for the OpenTelemetry tracer.
// [#extension: envoy.tracers.opentelemetry]
-// [#next-free-field: 6]
+// [#next-free-field: 7]
message OpenTelemetryConfig {
// The upstream gRPC cluster that will receive OTLP traces.
// Note that the tracer drops traces if the server does not read data fast enough.
@@ -57,4 +59,9 @@ message OpenTelemetryConfig {
// See: `OpenTelemetry sampler specification `_
// [#extension-category: envoy.tracers.opentelemetry.samplers]
core.v3.TypedExtensionConfig sampler = 5;
+
+ // Envoy caches the span in memory when the OpenTelemetry backend service is temporarily unavailable.
+ // This field specifies the maximum number of spans that can be cached. If not specified, the
+ // default is 1024.
+ google.protobuf.UInt32Value max_cache_size = 6;
}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/config/trace/v3/zipkin.proto b/xds/third_party/envoy/src/main/proto/envoy/config/trace/v3/zipkin.proto
index 2d8f3195c31..7405c596ed5 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/config/trace/v3/zipkin.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/config/trace/v3/zipkin.proto
@@ -2,13 +2,14 @@ syntax = "proto3";
package envoy.config.trace.v3;
+import "envoy/config/core/v3/http_service.proto";
+
import "google/protobuf/wrappers.proto";
import "envoy/annotations/deprecation.proto";
import "udpa/annotations/migrate.proto";
import "udpa/annotations/status.proto";
import "udpa/annotations/versioning.proto";
-import "validate/validate.proto";
option java_package = "io.envoyproxy.envoy.config.trace.v3";
option java_outer_classname = "ZipkinProto";
@@ -21,10 +22,22 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// Configuration for the Zipkin tracer.
// [#extension: envoy.tracers.zipkin]
-// [#next-free-field: 8]
+// [#next-free-field: 10]
message ZipkinConfig {
option (udpa.annotations.versioning).previous_message_type = "envoy.config.trace.v2.ZipkinConfig";
+ // Available trace context options for handling different trace header formats.
+ enum TraceContextOption {
+ // Use B3 headers only (default behavior).
+ USE_B3 = 0;
+
+ // Enable B3 and W3C dual header support:
+ // - For downstream: Extract from B3 headers first, fallback to W3C traceparent if B3 is unavailable.
+ // - For upstream: Inject both B3 and W3C traceparent headers.
+ // When this option is NOT set, only B3 headers are used for both extraction and injection.
+ USE_B3_WITH_W3C_PROPAGATION = 1;
+ }
+
// Available Zipkin collector endpoint versions.
enum CollectorEndpointVersion {
// Zipkin API v1, JSON over HTTP.
@@ -48,11 +61,17 @@ message ZipkinConfig {
}
// The cluster manager cluster that hosts the Zipkin collectors.
- string collector_cluster = 1 [(validate.rules).string = {min_len: 1}];
+ // Note: This field will be deprecated in future releases in favor of
+ // :ref:`collector_service `.
+ // Either this field or collector_service must be specified.
+ string collector_cluster = 1;
// The API endpoint of the Zipkin service where the spans will be sent. When
// using a standard Zipkin installation.
- string collector_endpoint = 2 [(validate.rules).string = {min_len: 1}];
+ // Note: This field will be deprecated in future releases in favor of
+ // :ref:`collector_service `.
+ // Required when using collector_cluster.
+ string collector_endpoint = 2;
// Determines whether a 128bit trace id will be used when creating a new
// trace instance. The default value is false, which will result in a 64 bit trace id being used.
@@ -67,6 +86,8 @@ message ZipkinConfig {
// Optional hostname to use when sending spans to the collector_cluster. Useful for collectors
// that require a specific hostname. Defaults to :ref:`collector_cluster ` above.
+ // Note: This field will be deprecated in future releases in favor of
+ // :ref:`collector_service `.
string collector_hostname = 6;
// If this is set to true, then Envoy will be treated as an independent hop in trace chain. A complete span pair will be created for a single
@@ -88,4 +109,60 @@ message ZipkinConfig {
// Please use that ``spawn_upstream_span`` field to control the span creation.
bool split_spans_for_request = 7
[deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];
+
+ // Determines which trace context format to use for trace header extraction and propagation.
+ // This controls both downstream request header extraction and upstream request header injection.
+ // Here is the spec for W3C trace headers: https://www.w3.org/TR/trace-context/
+ // The default value is USE_B3 to maintain backward compatibility.
+ TraceContextOption trace_context_option = 8;
+
+ // HTTP service configuration for the Zipkin collector.
+ // When specified, this configuration takes precedence over the legacy fields:
+ // collector_cluster, collector_endpoint, and collector_hostname.
+ // This provides a complete HTTP service configuration including cluster, URI, timeout, and headers.
+ // If not specified, the legacy fields above will be used for backward compatibility.
+ //
+ // Required fields when using collector_service:
+ //
+ // * ``http_uri.cluster`` - Must be specified and non-empty
+ // * ``http_uri.uri`` - Must be specified and non-empty
+ // * ``http_uri.timeout`` - Optional
+ //
+ // Full URI Support with Automatic Parsing:
+ //
+ // The ``uri`` field supports both path-only and full URI formats:
+ //
+ // .. code-block:: yaml
+ //
+ // tracing:
+ // provider:
+ // name: envoy.tracers.zipkin
+ // typed_config:
+ // "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig
+ // collector_service:
+ // http_uri:
+ // # Full URI format - hostname and path are extracted automatically
+ // uri: "https://zipkin-collector.example.com/api/v2/spans"
+ // cluster: zipkin
+ // timeout: 5s
+ // request_headers_to_add:
+ // - header:
+ // key: "X-Custom-Token"
+ // value: "your-custom-token"
+ // - header:
+ // key: "X-Service-ID"
+ // value: "your-service-id"
+ //
+ // URI Parsing Behavior:
+ //
+ // * Full URI: ``"https://zipkin-collector.example.com/api/v2/spans"``
+ //
+ // * Hostname: ``zipkin-collector.example.com`` (sets HTTP ``Host`` header)
+ // * Path: ``/api/v2/spans`` (sets HTTP request path)
+ //
+ // * Path only: ``"/api/v2/spans"``
+ //
+ // * Hostname: Uses cluster name as fallback
+ // * Path: ``/api/v2/spans``
+ core.v3.HttpService collector_service = 9;
}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto
new file mode 100644
index 00000000000..7cf2aac64aa
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto
@@ -0,0 +1,529 @@
+syntax = "proto3";
+
+package envoy.extensions.filters.http.ext_authz.v3;
+
+import "envoy/config/common/mutation_rules/v3/mutation_rules.proto";
+import "envoy/config/core/v3/base.proto";
+import "envoy/config/core/v3/config_source.proto";
+import "envoy/config/core/v3/grpc_service.proto";
+import "envoy/config/core/v3/http_uri.proto";
+import "envoy/type/matcher/v3/metadata.proto";
+import "envoy/type/matcher/v3/string.proto";
+import "envoy/type/v3/http_status.proto";
+
+import "google/protobuf/struct.proto";
+import "google/protobuf/wrappers.proto";
+
+import "envoy/annotations/deprecation.proto";
+import "udpa/annotations/sensitive.proto";
+import "udpa/annotations/status.proto";
+import "udpa/annotations/versioning.proto";
+import "validate/validate.proto";
+
+option java_package = "io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3";
+option java_outer_classname = "ExtAuthzProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3;ext_authzv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: External Authorization]
+// External Authorization :ref:`configuration overview `.
+// [#extension: envoy.filters.http.ext_authz]
+
+// [#next-free-field: 30]
+message ExtAuthz {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.config.filter.http.ext_authz.v3.ExtAuthz";
+
+ reserved 4;
+
+ reserved "use_alpha";
+
+ // External authorization service configuration.
+ oneof services {
+ // gRPC service configuration (default timeout: 200ms).
+ config.core.v3.GrpcService grpc_service = 1;
+
+ // HTTP service configuration (default timeout: 200ms).
+ HttpService http_service = 3;
+ }
+
+ // API version for ext_authz transport protocol. This describes the ext_authz gRPC endpoint and
+ // version of messages used on the wire.
+ config.core.v3.ApiVersion transport_api_version = 12
+ [(validate.rules).enum = {defined_only: true}];
+
+ // Changes the filter's behavior on errors:
+ //
+ // #. When set to ``true``, the filter will ``accept`` the client request even if communication with
+ // the authorization service has failed, or if the authorization service has returned an HTTP 5xx
+ // error.
+ //
+ // #. When set to ``false``, the filter will ``reject`` client requests and return ``Forbidden``
+ // if communication with the authorization service has failed, or if the authorization service
+ // has returned an HTTP 5xx error.
+ //
+ // Errors can always be tracked in the :ref:`stats `.
+ bool failure_mode_allow = 2;
+
+ // When ``failure_mode_allow`` and ``failure_mode_allow_header_add`` are both set to ``true``,
+ // ``x-envoy-auth-failure-mode-allowed: true`` will be added to request headers if the communication
+ // with the authorization service has failed, or if the authorization service has returned a
+ // HTTP 5xx error.
+ bool failure_mode_allow_header_add = 19;
+
+ // Enables the filter to buffer the client request body and send it within the authorization request.
+ // The ``x-envoy-auth-partial-body: false|true`` metadata header will be added to the authorization
+ // request indicating whether the body data is partial.
+ BufferSettings with_request_body = 5;
+
+ // Clears the route cache in order to allow the external authorization service to correctly affect
+ // routing decisions. The filter clears all cached routes when:
+ //
+ // #. The field is set to ``true``.
+ //
+ // #. The status returned from the authorization service is an HTTP 200 or gRPC 0.
+ //
+ // #. At least one ``authorization response header`` is added to the client request, or is used to
+ // alter another client request header.
+ //
+ bool clear_route_cache = 6;
+
+ // Sets the HTTP status that is returned to the client when the authorization server returns an error
+ // or cannot be reached. The default status is HTTP 403 Forbidden.
+ type.v3.HttpStatus status_on_error = 7;
+
+ // When this is set to ``true``, the filter will check the :ref:`ext_authz response
+ // ` for invalid header and
+ // query parameter mutations. If the side stream response is invalid, it will send a local reply
+ // to the downstream request with status HTTP 500 Internal Server Error.
+ //
+ // .. note::
+ // Both ``headers_to_remove`` and ``query_parameters_to_remove`` are validated, but invalid elements in
+ // those fields should not affect any headers and thus will not cause the filter to send a local reply.
+ //
+ // When set to ``false``, any invalid mutations will be visible to the rest of Envoy and may cause
+ // unexpected behavior.
+ //
+ // If you are using ext_authz with an untrusted ext_authz server, you should set this to ``true``.
+ bool validate_mutations = 24;
+
+ // Specifies a list of metadata namespaces whose values, if present, will be passed to the
+ // ext_authz service. The :ref:`filter_metadata `
+ // is passed as an opaque ``protobuf::Struct``.
+ //
+ // .. note::
+ // This field applies exclusively to the gRPC ext_authz service and has no effect on the HTTP service.
+ //
+ // For example, if the ``jwt_authn`` filter is used and :ref:`payload_in_metadata
+ // ` is set,
+ // then the following will pass the jwt payload to the authorization server.
+ //
+ // .. code-block:: yaml
+ //
+ // metadata_context_namespaces:
+ // - envoy.filters.http.jwt_authn
+ //
+ repeated string metadata_context_namespaces = 8;
+
+ // Specifies a list of metadata namespaces whose values, if present, will be passed to the
+ // ext_authz service. :ref:`typed_filter_metadata `
+ // is passed as a ``protobuf::Any``.
+ //
+ // .. note::
+ // This field applies exclusively to the gRPC ext_authz service and has no effect on the HTTP service.
+ //
+ // This works similarly to ``metadata_context_namespaces`` but allows Envoy and the ext_authz server to share
+ // the protobuf message definition in order to perform safe parsing.
+ //
+ repeated string typed_metadata_context_namespaces = 16;
+
+ // Specifies a list of route metadata namespaces whose values, if present, will be passed to the
+ // ext_authz service at :ref:`route_metadata_context ` in
+ // :ref:`CheckRequest `.
+ // :ref:`filter_metadata ` is passed as an opaque ``protobuf::Struct``.
+ repeated string route_metadata_context_namespaces = 21;
+
+ // Specifies a list of route metadata namespaces whose values, if present, will be passed to the
+ // ext_authz service at :ref:`route_metadata_context ` in
+ // :ref:`CheckRequest `.
+ // :ref:`typed_filter_metadata ` is passed as a ``protobuf::Any``.
+ repeated string route_typed_metadata_context_namespaces = 22;
+
+ // Specifies if the filter is enabled.
+ //
+ // If :ref:`runtime_key ` is specified,
+ // Envoy will lookup the runtime key to get the percentage of requests to filter.
+ //
+ // If this field is not specified, the filter will be enabled for all requests.
+ config.core.v3.RuntimeFractionalPercent filter_enabled = 9;
+
+ // Specifies if the filter is enabled with metadata matcher.
+ // If this field is not specified, the filter will be enabled for all requests.
+ type.matcher.v3.MetadataMatcher filter_enabled_metadata = 14;
+
+ // Specifies whether to deny the requests when the filter is disabled.
+ // If :ref:`runtime_key ` is specified,
+ // Envoy will lookup the runtime key to determine whether to deny requests for filter-protected paths
+ // when the filter is disabled. If the filter is disabled in ``typed_per_filter_config`` for the path,
+ // requests will not be denied.
+ //
+ // If this field is not specified, all requests will be allowed when disabled.
+ //
+ // If a request is denied due to this setting, the response code in :ref:`status_on_error
+ // ` will
+ // be returned.
+ config.core.v3.RuntimeFeatureFlag deny_at_disable = 11;
+
+ // Specifies if the peer certificate is sent to the external service.
+ //
+ // When this field is ``true``, Envoy will include the peer X.509 certificate, if available, in the
+ // :ref:`certificate`.
+ bool include_peer_certificate = 10;
+
+ // Optional additional prefix to use when emitting statistics. This allows distinguishing
+ // emitted statistics between configured ``ext_authz`` filters in an HTTP filter chain. For example:
+ //
+ // .. code-block:: yaml
+ //
+ // http_filters:
+ // - name: envoy.filters.http.ext_authz
+ // typed_config:
+ // "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
+ // stat_prefix: waf # This emits ext_authz.waf.ok, ext_authz.waf.denied, etc.
+ // - name: envoy.filters.http.ext_authz
+ // typed_config:
+ // "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
+ // stat_prefix: blocker # This emits ext_authz.blocker.ok, ext_authz.blocker.denied, etc.
+ //
+ string stat_prefix = 13;
+
+ // Optional labels that will be passed to :ref:`labels` in
+ // :ref:`destination`.
+ // The labels will be read from :ref:`metadata` with the specified key.
+ string bootstrap_metadata_labels_key = 15;
+
+ // Check request to authorization server will include the client request headers that have a correspondent match
+ // in the :ref:`list `. If this option isn't specified, then
+ // all client request headers are included in the check request to a gRPC authorization server, whereas no client request headers
+ // (besides the ones allowed by default - see note below) are included in the check request to an HTTP authorization server.
+ // This inconsistency between gRPC and HTTP servers is to maintain backwards compatibility with legacy behavior.
+ //
+ // .. note::
+ //
+ // For requests to an HTTP authorization server: in addition to the user's supplied matchers, ``Host``, ``Method``, ``Path``,
+ // ``Content-Length``, and ``Authorization`` are **additionally included** in the list.
+ //
+ // .. note::
+ //
+ // For requests to an HTTP authorization server: the value of ``Content-Length`` will be set to ``0`` and the request to the
+ // authorization server will not have a message body. However, the check request can include the buffered
+ // client request body (controlled by :ref:`with_request_body
+ // ` setting);
+ // consequently, the value of ``Content-Length`` in the authorization request reflects the size of its payload.
+ //
+ // .. note::
+ //
+ // This can be overridden by the field ``disallowed_headers`` below. That is, if a header
+ // matches for both ``allowed_headers`` and ``disallowed_headers``, the header will NOT be sent.
+ type.matcher.v3.ListStringMatcher allowed_headers = 17;
+
+ // If set, specifically disallow any header in this list to be forwarded to the external
+ // authentication server. This overrides the above ``allowed_headers`` if a header matches both.
+ type.matcher.v3.ListStringMatcher disallowed_headers = 25;
+
+ // Specifies if the TLS session level details like SNI are sent to the external service.
+ //
+ // When this field is ``true``, Envoy will include the SNI name used for TLSClientHello, if available, in the
+ // :ref:`tls_session`.
+ bool include_tls_session = 18;
+
+ // Whether to increment cluster statistics (e.g. cluster..upstream_rq_*) on authorization failure.
+ // Defaults to ``true``.
+ google.protobuf.BoolValue charge_cluster_response_stats = 20;
+
+ // Whether to encode the raw headers (i.e., unsanitized values and unconcatenated multi-line headers)
+ // in the authorization request. Works with both HTTP and gRPC clients.
+ //
+ // When this is set to ``true``, header values are not sanitized. Headers with the same key will also
+ // not be combined into a single, comma-separated header.
+ // Requests to gRPC services will populate the field
+ // :ref:`header_map`.
+ // Requests to HTTP services will be constructed with the unsanitized header values and preserved
+ // multi-line headers with the same key.
+ //
+ // If this field is set to ``false``, header values will be sanitized, with any non-UTF-8-compliant
+ // bytes replaced with ``'!'``. Headers with the same key will have their values concatenated into a
+ // single comma-separated header value.
+ // Requests to gRPC services will populate the field
+ // :ref:`headers`.
+ // Requests to HTTP services will have their header values sanitized and will not preserve
+ // multi-line headers with the same key.
+ //
+ // It is recommended to set this to ``true`` unless you rely on the previous behavior.
+ //
+ // It is set to ``false`` by default for backwards compatibility.
+ bool encode_raw_headers = 23;
+
+ // Rules for what modifications an ext_authz server may make to the request headers before
+ // continuing decoding / forwarding upstream.
+ //
+ // If set to anything, enables header mutation checking against configured rules. Note that
+ // :ref:`HeaderMutationRules `
+ // has defaults that change ext_authz behavior. Also note that if this field is set to anything,
+ // ext_authz can no longer append to :-prefixed headers.
+ //
+ // If empty, header mutation rule checking is completely disabled.
+ //
+ // Regardless of what is configured here, ext_authz cannot remove :-prefixed headers.
+ //
+ // This field and ``validate_mutations`` have different use cases. ``validate_mutations`` enables
+ // correctness checks for all header / query parameter mutations (e.g. for invalid characters).
+ // This field allows the filter to reject mutations to specific headers.
+ config.common.mutation_rules.v3.HeaderMutationRules decoder_header_mutation_rules = 26;
+
+ // Enable or disable ingestion of dynamic metadata from the ext_authz service.
+ //
+ // If ``false``, the filter will ignore dynamic metadata injected by the ext_authz service. If the
+ // ext_authz service tries injecting dynamic metadata, the filter will log, increment the
+ // ``ignored_dynamic_metadata`` stat, then continue handling the response.
+ //
+ // If ``true``, the filter will ingest dynamic metadata entries as normal.
+ //
+ // If unset, defaults to ``true``.
+ google.protobuf.BoolValue enable_dynamic_metadata_ingestion = 27;
+
+ // Additional metadata to be added to the filter state for logging purposes. The metadata will be
+ // added to StreamInfo's filter state under the namespace corresponding to the ext_authz filter
+ // name.
+ google.protobuf.Struct filter_metadata = 28;
+
+ // When set to ``true``, the filter will emit per-stream stats for access logging. The filter state
+ // key will be the same as the filter name.
+ //
+ // If using Envoy gRPC, emits latency, bytes sent / received, upstream info, and upstream cluster
+ // info. If not using Envoy gRPC, emits only latency. Note that stats are ONLY added to filter
+ // state if a check request is actually made to an ext_authz service.
+ //
+ // If this is ``false`` the filter will not emit stats, but filter_metadata will still be respected if
+ // it has a value.
+ //
+ // Field ``latency_us`` is exposed for CEL and logging when using gRPC or HTTP service.
+ // Fields ``bytesSent`` and ``bytesReceived`` are exposed for CEL and logging only when using gRPC service.
+ bool emit_filter_state_stats = 29;
+}
+
+// Configuration for buffering the request data.
+message BufferSettings {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.config.filter.http.ext_authz.v2.BufferSettings";
+
+ // Sets the maximum size of a message body that the filter will hold in memory. Envoy will return
+ // ``HTTP 413`` and will *not* initiate the authorization process when the buffer reaches the size
+ // set in this field. Note that this setting will have precedence over :ref:`failure_mode_allow
+ // `.
+ uint32 max_request_bytes = 1 [(validate.rules).uint32 = {gt: 0}];
+
+ // When this field is ``true``, Envoy will buffer the message until ``max_request_bytes`` is reached.
+ // The authorization request will be dispatched and no 413 HTTP error will be returned by the
+ // filter.
+ bool allow_partial_message = 2;
+
+ // If ``true``, the body sent to the external authorization service is set as raw bytes and populates
+ // :ref:`raw_body`
+ // in the HTTP request attribute context. Otherwise, :ref:`body
+ // ` will be populated
+ // with a UTF-8 string request body.
+ //
+ // This field only affects configurations using a :ref:`grpc_service
+ // `. In configurations that use
+ // an :ref:`http_service `, this
+ // has no effect.
+ bool pack_as_bytes = 3;
+}
+
+// HttpService is used for raw HTTP communication between the filter and the authorization service.
+// When configured, the filter will parse the client request and use these attributes to call the
+// authorization server. Depending on the response, the filter may reject or accept the client
+// request. Note that in any of these events, metadata can be added, removed or overridden by the
+// filter:
+//
+// On authorization request, a list of allowed request headers may be supplied. See
+// :ref:`allowed_headers
+// `
+// for details. Additional headers metadata may be added to the authorization request. See
+// :ref:`headers_to_add
+// ` for
+// details.
+//
+// On authorization response status ``HTTP 200 OK``, the filter will allow traffic to the upstream and
+// additional headers metadata may be added to the original client request. See
+// :ref:`allowed_upstream_headers
+// `
+// for details. Additionally, the filter may add additional headers to the client's response. See
+// :ref:`allowed_client_headers_on_success
+// `
+// for details.
+//
+// On other authorization response statuses, the filter will not allow traffic. Additional headers
+// metadata as well as body may be added to the client's response. See :ref:`allowed_client_headers
+// `
+// for details.
+// [#next-free-field: 9]
+message HttpService {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.config.filter.http.ext_authz.v2.HttpService";
+
+ reserved 3, 4, 5, 6;
+
+ // Sets the HTTP server URI which the authorization requests must be sent to.
+ config.core.v3.HttpUri server_uri = 1;
+
+ // Sets a prefix to the value of authorization request header ``Path``.
+ string path_prefix = 2;
+
+ // Settings used for controlling authorization request metadata.
+ AuthorizationRequest authorization_request = 7;
+
+ // Settings used for controlling authorization response metadata.
+ AuthorizationResponse authorization_response = 8;
+}
+
+message AuthorizationRequest {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.config.filter.http.ext_authz.v2.AuthorizationRequest";
+
+ // Authorization request includes the client request headers that have a corresponding match
+ // in the :ref:`list `.
+ // This field has been deprecated in favor of :ref:`allowed_headers
+ // `.
+ //
+ // .. note::
+ //
+ // In addition to the user's supplied matchers, ``Host``, ``Method``, ``Path``,
+ // ``Content-Length``, and ``Authorization`` are **automatically included** in the list.
+ //
+ // .. note::
+ //
+ // By default, the ``Content-Length`` header is set to ``0`` and the request to the authorization
+ // service has no message body. However, the authorization request *may* include the buffered
+ // client request body (controlled by :ref:`with_request_body
+ // `
+ // setting); hence the value of its ``Content-Length`` reflects the size of its payload.
+ //
+ type.matcher.v3.ListStringMatcher allowed_headers = 1
+ [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];
+
+ // Sets a list of headers that will be included in the request to the authorization service. Note that
+ // client request headers with the same key will be overridden.
+ repeated config.core.v3.HeaderValue headers_to_add = 2;
+}
+
+// [#next-free-field: 6]
+message AuthorizationResponse {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.config.filter.http.ext_authz.v2.AuthorizationResponse";
+
+ // When this :ref:`list ` is set, authorization
+ // response headers that have a correspondent match will be added to the original client request.
+ // Note that coexistent headers will be overridden.
+ type.matcher.v3.ListStringMatcher allowed_upstream_headers = 1;
+
+ // When this :ref:`list ` is set, authorization
+ // response headers that have a correspondent match will be added to the original client request.
+ // Note that coexistent headers will be appended.
+ type.matcher.v3.ListStringMatcher allowed_upstream_headers_to_append = 3;
+
+ // When this :ref:`list ` is set, authorization
+ // response headers that have a correspondent match will be added to the client's response. Note
+ // that when this list is *not* set, all the authorization response headers, except ``Authority
+ // (Host)`` will be in the response to the client. When a header is included in this list, ``Path``,
+ // ``Status``, ``Content-Length``, ``WWWAuthenticate`` and ``Location`` are automatically added.
+ type.matcher.v3.ListStringMatcher allowed_client_headers = 2;
+
+ // When this :ref:`list ` is set, authorization
+ // response headers that have a correspondent match will be added to the client's response when
+ // the authorization response itself is successful, i.e. not failed or denied. When this list is
+ // *not* set, no additional headers will be added to the client's response on success.
+ type.matcher.v3.ListStringMatcher allowed_client_headers_on_success = 4;
+
+ // When this :ref:`list ` is set, authorization
+ // response headers that have a correspondent match will be emitted as dynamic metadata to be consumed
+ // by the next filter. This metadata lives in a namespace specified by the canonical name of extension filter
+ // that requires it:
+ //
+ // - :ref:`envoy.filters.http.ext_authz ` for HTTP filter.
+ // - :ref:`envoy.filters.network.ext_authz ` for network filter.
+ type.matcher.v3.ListStringMatcher dynamic_metadata_from_headers = 5;
+}
+
+// Extra settings on a per virtualhost/route/weighted-cluster level.
+message ExtAuthzPerRoute {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.config.filter.http.ext_authz.v2.ExtAuthzPerRoute";
+
+ oneof override {
+ option (validate.required) = true;
+
+ // Disable the ext auth filter for this particular vhost or route.
+ // If disabled is specified in multiple per-filter-configs, the most specific one will be used.
+ // If the filter is disabled by default and this is set to ``false``, the filter will be enabled
+ // for this vhost or route.
+ bool disabled = 1;
+
+ // Check request settings for this route.
+ CheckSettings check_settings = 2 [(validate.rules).message = {required: true}];
+ }
+}
+
+// Extra settings for the check request.
+// [#next-free-field: 6]
+message CheckSettings {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.config.filter.http.ext_authz.v2.CheckSettings";
+
+ // Context extensions to set on the CheckRequest's
+ // :ref:`AttributeContext.context_extensions`
+ //
+ // You can use this to provide extra context for the external authorization server on specific
+ // virtual hosts/routes. For example, adding a context extension on the virtual host level can
+ // give the ext-authz server information on what virtual host is used without needing to parse the
+ // host header. If CheckSettings is specified in multiple per-filter-configs, they will be merged
+ // in order, and the result will be used.
+ //
+ // Merge semantics for this field are such that keys from more specific configs override.
+ //
+ // .. note::
+ // These settings are only applied to a filter configured with a
+ // :ref:`grpc_service`.
+ map context_extensions = 1 [(udpa.annotations.sensitive) = true];
+
+ // When set to ``true``, disable the configured :ref:`with_request_body
+ // ` for a specific route.
+ //
+ // Only one of ``disable_request_body_buffering`` and
+ // :ref:`with_request_body `
+ // may be specified.
+ bool disable_request_body_buffering = 2;
+
+ // Enable or override request body buffering, which is configured using the
+ // :ref:`with_request_body `
+ // option for a specific route.
+ //
+ // Only one of ``with_request_body`` and
+ // :ref:`disable_request_body_buffering `
+ // may be specified.
+ BufferSettings with_request_body = 3;
+
+ // Override the external authorization service for this route.
+ // This allows different routes to use different external authorization service backends
+ // and service types (gRPC or HTTP). If specified, this overrides the filter-level service
+ // configuration regardless of the original service type.
+ oneof service_override {
+ // Override with a gRPC service configuration.
+ config.core.v3.GrpcService grpc_service = 4;
+
+ // Override with an HTTP service configuration.
+ HttpService http_service = 5;
+ }
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto
index e0282af86e6..730e065e6c4 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto
@@ -37,7 +37,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// HTTP connection manager :ref:`configuration overview `.
// [#extension: envoy.filters.network.http_connection_manager]
-// [#next-free-field: 59]
+// [#next-free-field: 60]
message HttpConnectionManager {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager";
@@ -527,16 +527,6 @@ message HttpConnectionManager {
// is terminated with a 408 Request Timeout error code if no upstream response
// header has been received, otherwise a stream reset occurs.
//
- // This timeout also specifies the amount of time that Envoy will wait for the peer to open enough
- // window to write any remaining stream data once the entirety of stream data (local end stream is
- // true) has been buffered pending available window. In other words, this timeout defends against
- // a peer that does not release enough window to completely write the stream, even though all
- // data has been proxied within available flow control windows. If the timeout is hit in this
- // case, the :ref:`tx_flush_timeout ` counter will be
- // incremented. Note that :ref:`max_stream_duration
- // ` does not apply to
- // this corner case.
- //
// If the :ref:`overload action ` "envoy.overload_actions.reduce_timeouts"
// is configured, this timeout is scaled according to the value for
// :ref:`HTTP_DOWNSTREAM_STREAM_IDLE `.
@@ -549,9 +539,29 @@ message HttpConnectionManager {
//
// A value of 0 will completely disable the connection manager stream idle
// timeout, although per-route idle timeout overrides will continue to apply.
+ //
+ // This timeout is also used as the default value for :ref:`stream_flush_timeout
+ // `.
google.protobuf.Duration stream_idle_timeout = 24
[(udpa.annotations.security).configure_for_untrusted_downstream = true];
+ // The stream flush timeout for connections managed by the connection manager.
+ //
+ // If not specified, the value of stream_idle_timeout is used. This is for backwards compatibility
+ // since this was the original behavior. In essence this timeout is an override for the
+ // stream_idle_timeout that applies specifically to the end of stream flush case.
+ //
+ // This timeout specifies the amount of time that Envoy will wait for the peer to open enough
+ // window to write any remaining stream data once the entirety of stream data (local end stream is
+ // true) has been buffered pending available window. In other words, this timeout defends against
+ // a peer that does not release enough window to completely write the stream, even though all
+ // data has been proxied within available flow control windows. If the timeout is hit in this
+ // case, the :ref:`tx_flush_timeout ` counter will be
+ // incremented. Note that :ref:`max_stream_duration
+ // ` does not apply to
+ // this corner case.
+ google.protobuf.Duration stream_flush_timeout = 59;
+
// The amount of time that Envoy will wait for the entire request to be received.
// The timer is activated when the request is initiated, and is disarmed when the last byte of the
// request is sent upstream (i.e. all decoding filters have processed the request), OR when the
@@ -1036,7 +1046,7 @@ message Rds {
"envoy.config.filter.network.http_connection_manager.v2.Rds";
// Configuration source specifier for RDS.
- config.core.v3.ConfigSource config_source = 1 [(validate.rules).message = {required: true}];
+ config.core.v3.ConfigSource config_source = 1;
// The name of the route configuration. This name will be passed to the RDS
// API. This allows an Envoy configuration with multiple HTTP listeners (and
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/call_credentials/access_token/v3/access_token_credentials.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/call_credentials/access_token/v3/access_token_credentials.proto
new file mode 100644
index 00000000000..45ee3839e6f
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/call_credentials/access_token/v3/access_token_credentials.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+package envoy.extensions.grpc_service.call_credentials.access_token.v3;
+
+import "udpa/annotations/status.proto";
+
+option java_package = "io.envoyproxy.envoy.extensions.grpc_service.call_credentials.access_token.v3";
+option java_outer_classname = "AccessTokenCredentialsProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/call_credentials/access_token/v3;access_tokenv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: gRPC Access Token Credentials]
+
+// [#not-implemented-hide:]
+message AccessTokenCredentials {
+ // The access token.
+ string token = 1;
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/google_default/v3/google_default_credentials.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/google_default/v3/google_default_credentials.proto
new file mode 100644
index 00000000000..77c3af41fdd
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/google_default/v3/google_default_credentials.proto
@@ -0,0 +1,17 @@
+syntax = "proto3";
+
+package envoy.extensions.grpc_service.channel_credentials.google_default.v3;
+
+import "udpa/annotations/status.proto";
+
+option java_package = "io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.google_default.v3";
+option java_outer_classname = "GoogleDefaultCredentialsProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/channel_credentials/google_default/v3;google_defaultv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: gRPC Google Default Credentials]
+
+// [#not-implemented-hide:]
+message GoogleDefaultCredentials {
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/insecure/v3/insecure_credentials.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/insecure/v3/insecure_credentials.proto
new file mode 100644
index 00000000000..70d58451e2d
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/insecure/v3/insecure_credentials.proto
@@ -0,0 +1,17 @@
+syntax = "proto3";
+
+package envoy.extensions.grpc_service.channel_credentials.insecure.v3;
+
+import "udpa/annotations/status.proto";
+
+option java_package = "io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.insecure.v3";
+option java_outer_classname = "InsecureCredentialsProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/channel_credentials/insecure/v3;insecurev3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: gRPC Insecure Credentials]
+
+// [#not-implemented-hide:]
+message InsecureCredentials {
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/local/v3/local_credentials.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/local/v3/local_credentials.proto
new file mode 100644
index 00000000000..00514a0e847
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/local/v3/local_credentials.proto
@@ -0,0 +1,17 @@
+syntax = "proto3";
+
+package envoy.extensions.grpc_service.channel_credentials.local.v3;
+
+import "udpa/annotations/status.proto";
+
+option java_package = "io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.local.v3";
+option java_outer_classname = "LocalCredentialsProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/channel_credentials/local/v3;localv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: gRPC Local Credentials]
+
+// [#not-implemented-hide:]
+message LocalCredentials {
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/tls/v3/tls_credentials.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/tls/v3/tls_credentials.proto
new file mode 100644
index 00000000000..f64c16bb684
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/tls/v3/tls_credentials.proto
@@ -0,0 +1,27 @@
+syntax = "proto3";
+
+package envoy.extensions.grpc_service.channel_credentials.tls.v3;
+
+import "envoy/extensions/transport_sockets/tls/v3/tls.proto";
+
+import "udpa/annotations/status.proto";
+
+option java_package = "io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.tls.v3";
+option java_outer_classname = "TlsCredentialsProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/channel_credentials/tls/v3;tlsv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: gRPC TLS Credentials]
+
+// [#not-implemented-hide:]
+message TlsCredentials {
+ // The certificate provider instance for the root cert. Must be set.
+ transport_sockets.tls.v3.CommonTlsContext.CertificateProviderInstance root_certificate_provider =
+ 1;
+
+ // The certificate provider instance for the identity cert. Optional;
+ // if unset, no identity certificate will be sent to the server.
+ transport_sockets.tls.v3.CommonTlsContext.CertificateProviderInstance
+ identity_certificate_provider = 2;
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/xds/v3/xds_credentials.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/xds/v3/xds_credentials.proto
new file mode 100644
index 00000000000..ba8d471dd49
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/grpc_service/channel_credentials/xds/v3/xds_credentials.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+
+package envoy.extensions.grpc_service.channel_credentials.xds.v3;
+
+import "google/protobuf/any.proto";
+
+import "udpa/annotations/status.proto";
+
+option java_package = "io.envoyproxy.envoy.extensions.grpc_service.channel_credentials.xds.v3";
+option java_outer_classname = "XdsCredentialsProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/channel_credentials/xds/v3;xdsv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: gRPC xDS Credentials]
+
+// [#not-implemented-hide:]
+message XdsCredentials {
+ // Fallback credentials. Required.
+ google.protobuf.Any fallback_credentials = 1;
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/extensions/load_balancing_policies/common/v3/common.proto b/xds/third_party/envoy/src/main/proto/envoy/extensions/load_balancing_policies/common/v3/common.proto
index 7868fb02b1a..22faf11b9c5 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/extensions/load_balancing_policies/common/v3/common.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/extensions/load_balancing_policies/common/v3/common.proto
@@ -3,6 +3,7 @@ syntax = "proto3";
package envoy.extensions.load_balancing_policies.common.v3;
import "envoy/config/core/v3/base.proto";
+import "envoy/config/route/v3/route_components.proto";
import "envoy/type/v3/percent.proto";
import "google/protobuf/duration.proto";
@@ -23,8 +24,17 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
message LocalityLbConfig {
// Configuration for :ref:`zone aware routing
// `.
- // [#next-free-field: 6]
+ // [#next-free-field: 7]
message ZoneAwareLbConfig {
+ // Basis for computing per-locality percentages in zone-aware routing.
+ enum LocalityBasis {
+ // Use the number of healthy hosts in each locality.
+ HEALTHY_HOSTS_NUM = 0;
+
+ // Use the weights of healthy hosts in each locality.
+ HEALTHY_HOSTS_WEIGHT = 1;
+ }
+
// Configures Envoy to always route requests to the local zone regardless of the
// upstream zone structure. In Envoy's default configuration, traffic is distributed proportionally
// across all upstream hosts while trying to maximize local routing when possible. The approach
@@ -66,6 +76,12 @@ message LocalityLbConfig {
[deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];
ForceLocalZone force_local_zone = 5;
+
+ // Determines how locality percentages are computed:
+ // - HEALTHY_HOSTS_NUM: proportional to the count of healthy hosts.
+ // - HEALTHY_HOSTS_WEIGHT: proportional to the weights of healthy hosts.
+ // Default value is HEALTHY_HOSTS_NUM if unset.
+ LocalityBasis locality_basis = 6;
}
// Configuration for :ref:`locality weighted load balancing
@@ -136,4 +152,10 @@ message ConsistentHashingLbConfig {
// This is an O(N) algorithm, unlike other load balancers. Using a lower ``hash_balance_factor`` results in more hosts
// being probed, so use a higher value if you require better performance.
google.protobuf.UInt32Value hash_balance_factor = 2 [(validate.rules).uint32 = {gte: 100}];
+
+ // Specifies a list of hash policies to use for ring hash load balancing. If ``hash_policy`` is
+ // set, then
+ // :ref:`route level hash policy `
+ // will be ignored.
+ repeated config.route.v3.RouteAction.HashPolicy hash_policy = 3;
}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/service/auth/v3/attribute_context.proto b/xds/third_party/envoy/src/main/proto/envoy/service/auth/v3/attribute_context.proto
new file mode 100644
index 00000000000..2c4fbb4b73e
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/service/auth/v3/attribute_context.proto
@@ -0,0 +1,222 @@
+syntax = "proto3";
+
+package envoy.service.auth.v3;
+
+import "envoy/config/core/v3/address.proto";
+import "envoy/config/core/v3/base.proto";
+
+import "google/protobuf/timestamp.proto";
+
+import "udpa/annotations/migrate.proto";
+import "udpa/annotations/status.proto";
+import "udpa/annotations/versioning.proto";
+
+option java_package = "io.envoyproxy.envoy.service.auth.v3";
+option java_outer_classname = "AttributeContextProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3;authv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: Attribute context]
+
+// See :ref:`network filter configuration overview `
+// and :ref:`HTTP filter configuration overview `.
+
+// An attribute is a piece of metadata that describes an activity on a network.
+// For example, the size of an HTTP request, or the status code of an HTTP response.
+//
+// Each attribute has a type and a name, which is logically defined as a proto message field
+// of the ``AttributeContext``. The ``AttributeContext`` is a collection of individual attributes
+// supported by Envoy authorization system.
+// [#comment: The following items are left out of this proto
+// Request.Auth field for JWTs
+// Request.Api for api management
+// Origin peer that originated the request
+// Caching Protocol
+// request_context return values to inject back into the filter chain
+// peer.claims -- from X.509 extensions
+// Configuration
+// - field mask to send
+// - which return values from request_context are copied back
+// - which return values are copied into request_headers]
+// [#next-free-field: 14]
+message AttributeContext {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.service.auth.v2.AttributeContext";
+
+ // This message defines attributes for a node that handles a network request.
+ // The node can be either a service or an application that sends, forwards,
+ // or receives the request. Service peers should fill in the ``service``,
+ // ``principal``, and ``labels`` as appropriate.
+ // [#next-free-field: 6]
+ message Peer {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.service.auth.v2.AttributeContext.Peer";
+
+ // The address of the peer, this is typically the IP address.
+ // It can also be UDS path, or others.
+ config.core.v3.Address address = 1;
+
+ // The canonical service name of the peer.
+ // It should be set to :ref:`the HTTP x-envoy-downstream-service-cluster
+ // `
+ // If a more trusted source of the service name is available through mTLS/secure naming, it
+ // should be used.
+ string service = 2;
+
+ // The labels associated with the peer.
+ // These could be pod labels for Kubernetes or tags for VMs.
+ // The source of the labels could be an X.509 certificate or other configuration.
+ map labels = 3;
+
+ // The authenticated identity of this peer.
+ // For example, the identity associated with the workload such as a service account.
+ // If an X.509 certificate is used to assert the identity this field should be sourced from
+ // ``URI Subject Alternative Names``, ``DNS Subject Alternate Names`` or ``Subject`` in that order.
+ // The primary identity should be the principal. The principal format is issuer specific.
+ //
+ // Examples:
+ //
+ // - SPIFFE format is ``spiffe://trust-domain/path``.
+ // - Google account format is ``https://accounts.google.com/{userid}``.
+ string principal = 4;
+
+ // The X.509 certificate used to authenticate the identify of this peer.
+ // When present, the certificate contents are encoded in URL and PEM format.
+ string certificate = 5;
+ }
+
+ // Represents a network request, such as an HTTP request.
+ message Request {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.service.auth.v2.AttributeContext.Request";
+
+ // The timestamp when the proxy receives the first byte of the request.
+ google.protobuf.Timestamp time = 1;
+
+ // Represents an HTTP request or an HTTP-like request.
+ HttpRequest http = 2;
+ }
+
+ // This message defines attributes for an HTTP request.
+ // HTTP/1.x, HTTP/2, gRPC are all considered as HTTP requests.
+ // [#next-free-field: 14]
+ message HttpRequest {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.service.auth.v2.AttributeContext.HttpRequest";
+
+ // The unique ID for a request, which can be propagated to downstream
+ // systems. The ID should have low probability of collision
+ // within a single day for a specific service.
+ // For HTTP requests, it should be X-Request-ID or equivalent.
+ string id = 1;
+
+ // The HTTP request method, such as ``GET``, ``POST``.
+ string method = 2;
+
+ // The HTTP request headers. If multiple headers share the same key, they
+ // must be merged according to the HTTP spec. All header keys must be
+ // lower-cased, because HTTP header keys are case-insensitive.
+ // Header value is encoded as UTF-8 string. Non-UTF-8 characters will be replaced by "!".
+ // This field will not be set if
+ // :ref:`encode_raw_headers `
+ // is set to true.
+ map headers = 3
+ [(udpa.annotations.field_migrate).oneof_promotion = "headers_type"];
+
+ // A list of the raw HTTP request headers. This is used instead of
+ // :ref:`headers ` when
+ // :ref:`encode_raw_headers `
+ // is set to true.
+ //
+ // Note that this is not actually a map type. ``header_map`` contains a single repeated field
+ // ``headers``.
+ //
+ // Here, only the ``key`` and ``raw_value`` fields will be populated for each HeaderValue, and
+ // that is only when
+ // :ref:`encode_raw_headers `
+ // is set to true.
+ //
+ // Also, unlike the
+ // :ref:`headers `
+ // field, headers with the same key are not combined into a single comma separated header.
+ config.core.v3.HeaderMap header_map = 13
+ [(udpa.annotations.field_migrate).oneof_promotion = "headers_type"];
+
+ // The request target, as it appears in the first line of the HTTP request. This includes
+ // the URL path and query-string. No decoding is performed.
+ string path = 4;
+
+ // The HTTP request ``Host`` or ``:authority`` header value.
+ string host = 5;
+
+ // The HTTP URL scheme, such as ``http`` and ``https``.
+ string scheme = 6;
+
+ // This field is always empty, and exists for compatibility reasons. The HTTP URL query is
+ // included in ``path`` field.
+ string query = 7;
+
+ // This field is always empty, and exists for compatibility reasons. The URL fragment is
+ // not submitted as part of HTTP requests; it is unknowable.
+ string fragment = 8;
+
+ // The HTTP request size in bytes. If unknown, it must be -1.
+ int64 size = 9;
+
+ // The network protocol used with the request, such as "HTTP/1.0", "HTTP/1.1", or "HTTP/2".
+ //
+ // See :repo:`headers.h:ProtocolStrings ` for a list of all
+ // possible values.
+ string protocol = 10;
+
+ // The HTTP request body.
+ string body = 11;
+
+ // The HTTP request body in bytes. This is used instead of
+ // :ref:`body ` when
+ // :ref:`pack_as_bytes `
+ // is set to true.
+ bytes raw_body = 12;
+ }
+
+ // This message defines attributes for the underlying TLS session.
+ message TLSSession {
+ // SNI used for TLS session.
+ string sni = 1;
+ }
+
+ // The source of a network activity, such as starting a TCP connection.
+ // In a multi hop network activity, the source represents the sender of the
+ // last hop.
+ Peer source = 1;
+
+ // The destination of a network activity, such as accepting a TCP connection.
+ // In a multi hop network activity, the destination represents the receiver of
+ // the last hop.
+ Peer destination = 2;
+
+ // Represents a network request, such as an HTTP request.
+ Request request = 4;
+
+ // This is analogous to http_request.headers, however these contents will not be sent to the
+ // upstream server. Context_extensions provide an extension mechanism for sending additional
+ // information to the auth server without modifying the proto definition. It maps to the
+ // internal opaque context in the filter chain.
+ map context_extensions = 10;
+
+ // Dynamic metadata associated with the request.
+ config.core.v3.Metadata metadata_context = 11;
+
+ // Metadata associated with the selected route.
+ config.core.v3.Metadata route_metadata_context = 13;
+
+ // TLS session details of the underlying connection.
+ // This is not populated by default and will be populated only if the ext_authz filter has
+ // been specifically configured to include this information.
+ // For HTTP ext_authz, that requires :ref:`include_tls_session `
+ // to be set to true.
+ // For network ext_authz, that requires :ref:`include_tls_session `
+ // to be set to true.
+ TLSSession tls_session = 12;
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/service/auth/v3/external_auth.proto b/xds/third_party/envoy/src/main/proto/envoy/service/auth/v3/external_auth.proto
new file mode 100644
index 00000000000..1f3ed5787d8
--- /dev/null
+++ b/xds/third_party/envoy/src/main/proto/envoy/service/auth/v3/external_auth.proto
@@ -0,0 +1,144 @@
+syntax = "proto3";
+
+package envoy.service.auth.v3;
+
+import "envoy/config/core/v3/base.proto";
+import "envoy/service/auth/v3/attribute_context.proto";
+import "envoy/type/v3/http_status.proto";
+
+import "google/protobuf/struct.proto";
+import "google/rpc/status.proto";
+
+import "envoy/annotations/deprecation.proto";
+import "udpa/annotations/status.proto";
+import "udpa/annotations/versioning.proto";
+
+option java_package = "io.envoyproxy.envoy.service.auth.v3";
+option java_outer_classname = "ExternalAuthProto";
+option java_multiple_files = true;
+option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3;authv3";
+option (udpa.annotations.file_status).package_version_status = ACTIVE;
+
+// [#protodoc-title: Authorization service]
+
+// The authorization service request messages used by external authorization :ref:`network filter
+// ` and :ref:`HTTP filter `.
+
+// A generic interface for performing authorization check on incoming
+// requests to a networked service.
+service Authorization {
+ // Performs authorization check based on the attributes associated with the
+ // incoming request, and returns status `OK` or not `OK`.
+ rpc Check(CheckRequest) returns (CheckResponse) {
+ }
+}
+
+message CheckRequest {
+ option (udpa.annotations.versioning).previous_message_type = "envoy.service.auth.v2.CheckRequest";
+
+ // The request attributes.
+ AttributeContext attributes = 1;
+}
+
+// HTTP attributes for a denied response.
+message DeniedHttpResponse {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.service.auth.v2.DeniedHttpResponse";
+
+ // This field allows the authorization service to send an HTTP response status code to the
+ // downstream client. If not set, Envoy sends ``403 Forbidden`` HTTP status code by default.
+ type.v3.HttpStatus status = 1;
+
+ // This field allows the authorization service to send HTTP response headers
+ // to the downstream client. Note that the :ref:`append field in HeaderValueOption ` defaults to
+ // false when used in this message.
+ repeated config.core.v3.HeaderValueOption headers = 2;
+
+ // This field allows the authorization service to send a response body data
+ // to the downstream client.
+ string body = 3;
+}
+
+// HTTP attributes for an OK response.
+// [#next-free-field: 9]
+message OkHttpResponse {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.service.auth.v2.OkHttpResponse";
+
+ // HTTP entity headers in addition to the original request headers. This allows the authorization
+ // service to append, to add or to override headers from the original request before
+ // dispatching it to the upstream. Note that the :ref:`append field in HeaderValueOption ` defaults to
+ // false when used in this message. By setting the ``append`` field to ``true``,
+ // the filter will append the correspondent header value to the matched request header.
+ // By leaving ``append`` as false, the filter will either add a new header, or override an existing
+ // one if there is a match.
+ repeated config.core.v3.HeaderValueOption headers = 2;
+
+ // HTTP entity headers to remove from the original request before dispatching
+ // it to the upstream. This allows the authorization service to act on auth
+ // related headers (like ``Authorization``), process them, and consume them.
+ // Under this model, the upstream will either receive the request (if it's
+ // authorized) or not receive it (if it's not), but will not see headers
+ // containing authorization credentials.
+ //
+ // Pseudo headers (such as ``:authority``, ``:method``, ``:path`` etc), as well as
+ // the header ``Host``, may not be removed as that would make the request
+ // malformed. If mentioned in ``headers_to_remove`` these special headers will
+ // be ignored.
+ //
+ // When using the HTTP service this must instead be set by the HTTP
+ // authorization service as a comma separated list like so:
+ // ``x-envoy-auth-headers-to-remove: one-auth-header, another-auth-header``.
+ repeated string headers_to_remove = 5;
+
+ // This field has been deprecated in favor of :ref:`CheckResponse.dynamic_metadata
+ // `. Until it is removed,
+ // setting this field overrides :ref:`CheckResponse.dynamic_metadata
+ // `.
+ google.protobuf.Struct dynamic_metadata = 3
+ [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];
+
+ // This field allows the authorization service to send HTTP response headers
+ // to the downstream client on success. Note that the :ref:`append field in HeaderValueOption `
+ // defaults to false when used in this message.
+ repeated config.core.v3.HeaderValueOption response_headers_to_add = 6;
+
+ // This field allows the authorization service to set (and overwrite) query
+ // string parameters on the original request before it is sent upstream.
+ repeated config.core.v3.QueryParameter query_parameters_to_set = 7;
+
+ // This field allows the authorization service to specify which query parameters
+ // should be removed from the original request before it is sent upstream. Each
+ // element in this list is a case-sensitive query parameter name to be removed.
+ repeated string query_parameters_to_remove = 8;
+}
+
+// Intended for gRPC and Network Authorization servers ``only``.
+message CheckResponse {
+ option (udpa.annotations.versioning).previous_message_type =
+ "envoy.service.auth.v2.CheckResponse";
+
+ // Status ``OK`` allows the request. Any other status indicates the request should be denied, and
+ // for HTTP filter, if not overridden by :ref:`denied HTTP response status `
+ // Envoy sends ``403 Forbidden`` HTTP status code by default.
+ google.rpc.Status status = 1;
+
+ // An message that contains HTTP response attributes. This message is
+ // used when the authorization service needs to send custom responses to the
+ // downstream client or, to modify/add request headers being dispatched to the upstream.
+ oneof http_response {
+ // Supplies http attributes for a denied response.
+ DeniedHttpResponse denied_response = 2;
+
+ // Supplies http attributes for an ok response.
+ OkHttpResponse ok_response = 3;
+ }
+
+ // Optional response metadata that will be emitted as dynamic metadata to be consumed by the next
+ // filter. This metadata lives in a namespace specified by the canonical name of extension filter
+ // that requires it:
+ //
+ // - :ref:`envoy.filters.http.ext_authz ` for HTTP filter.
+ // - :ref:`envoy.filters.network.ext_authz ` for network filter.
+ google.protobuf.Struct dynamic_metadata = 4;
+}
diff --git a/xds/third_party/envoy/src/main/proto/envoy/type/matcher/v3/value.proto b/xds/third_party/envoy/src/main/proto/envoy/type/matcher/v3/value.proto
index d773c6057fc..8d65c457ccc 100644
--- a/xds/third_party/envoy/src/main/proto/envoy/type/matcher/v3/value.proto
+++ b/xds/third_party/envoy/src/main/proto/envoy/type/matcher/v3/value.proto
@@ -17,7 +17,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// [#protodoc-title: Value matcher]
-// Specifies the way to match a ProtobufWkt::Value. Primitive values and ListValue are supported.
+// Specifies the way to match a Protobuf::Value. Primitive values and ListValue are supported.
// StructValue is not supported and is always not matched.
// [#next-free-field: 8]
message ValueMatcher {