|
| 1 | +/* |
| 2 | + * Copyright 2025 The gRPC Authors |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +package io.grpc.xds; |
| 18 | + |
| 19 | +import com.google.protobuf.Any; |
| 20 | +import com.google.protobuf.InvalidProtocolBufferException; |
| 21 | +import com.google.protobuf.Message; |
| 22 | +import io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3.ExtAuthz; |
| 23 | +import io.envoyproxy.envoy.service.auth.v3.AuthorizationGrpc; |
| 24 | +import io.grpc.ClientInterceptor; |
| 25 | +import io.grpc.ServerInterceptor; |
| 26 | +import io.grpc.xds.internal.ThreadSafeRandom; |
| 27 | +import io.grpc.xds.internal.ThreadSafeRandom.ThreadSafeRandomImpl; |
| 28 | +import io.grpc.xds.internal.extauthz.BufferingAuthzClientCall; |
| 29 | +import io.grpc.xds.internal.extauthz.CheckRequestBuilder; |
| 30 | +import io.grpc.xds.internal.extauthz.CheckResponseHandler; |
| 31 | +import io.grpc.xds.internal.extauthz.ExtAuthzCertificateProvider; |
| 32 | +import io.grpc.xds.internal.extauthz.ExtAuthzClientInterceptor; |
| 33 | +import io.grpc.xds.internal.extauthz.ExtAuthzConfig; |
| 34 | +import io.grpc.xds.internal.extauthz.ExtAuthzParseException; |
| 35 | +import io.grpc.xds.internal.extauthz.ExtAuthzServerInterceptor; |
| 36 | +import io.grpc.xds.internal.extauthz.StubManager; |
| 37 | +import io.grpc.xds.internal.grpcservice.InsecureGrpcChannelFactory; |
| 38 | +import io.grpc.xds.internal.headermutations.HeaderMutationFilter; |
| 39 | +import io.grpc.xds.internal.headermutations.HeaderMutator; |
| 40 | +import java.util.concurrent.ScheduledExecutorService; |
| 41 | +import javax.annotation.Nullable; |
| 42 | + |
| 43 | +final class ExtAuthzFilter implements Filter { |
| 44 | + |
| 45 | + private static final String TYPE_URL = |
| 46 | + "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz"; |
| 47 | + |
| 48 | + private static final String TYPE_URL_OVERRIDE_CONFIG = |
| 49 | + "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute"; |
| 50 | + |
| 51 | + |
| 52 | + static final class ExtAuthzFilterConfig implements Filter.FilterConfig { |
| 53 | + |
| 54 | + private final ExtAuthzConfig extAuthzConfig; |
| 55 | + |
| 56 | + ExtAuthzFilterConfig(ExtAuthzConfig extAuthzConfig) { |
| 57 | + this.extAuthzConfig = extAuthzConfig; |
| 58 | + } |
| 59 | + |
| 60 | + public ExtAuthzConfig extAuthzConfig() { |
| 61 | + return extAuthzConfig; |
| 62 | + } |
| 63 | + |
| 64 | + @Override |
| 65 | + public String typeUrl() { |
| 66 | + return ExtAuthzFilter.TYPE_URL; |
| 67 | + } |
| 68 | + |
| 69 | + public static ExtAuthzFilterConfig fromProto(ExtAuthz extAuthzProto) |
| 70 | + throws ExtAuthzParseException { |
| 71 | + return new ExtAuthzFilterConfig(ExtAuthzConfig.fromProto(extAuthzProto)); |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + // Placeholder for the external authorization filter's override config. |
| 76 | + static final class ExtAuthzFilterConfigOverride implements Filter.FilterConfig { |
| 77 | + @Override |
| 78 | + public final String typeUrl() { |
| 79 | + return ExtAuthzFilter.TYPE_URL_OVERRIDE_CONFIG; |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + static final class Provider implements Filter.Provider { |
| 84 | + |
| 85 | + @Override |
| 86 | + public String[] typeUrls() { |
| 87 | + return new String[] {TYPE_URL, TYPE_URL_OVERRIDE_CONFIG}; |
| 88 | + } |
| 89 | + |
| 90 | + @Override |
| 91 | + public boolean isClientFilter() { |
| 92 | + return true; |
| 93 | + } |
| 94 | + |
| 95 | + @Override |
| 96 | + public boolean isServerFilter() { |
| 97 | + return true; |
| 98 | + } |
| 99 | + |
| 100 | + @Override |
| 101 | + public ExtAuthzFilter newInstance(String name) { |
| 102 | + // Create a dedicated scheduler for this filter instance's StubManager |
| 103 | + StubManager stubManager = StubManager.create(InsecureGrpcChannelFactory.getInstance()); |
| 104 | + return new ExtAuthzFilter(stubManager, ThreadSafeRandomImpl.INSTANCE, |
| 105 | + BufferingAuthzClientCall.FACTORY_INSTANCE, ExtAuthzCertificateProvider.create(), |
| 106 | + CheckRequestBuilder.INSTANCE, CheckResponseHandler.INSTANCE, |
| 107 | + ExtAuthzClientInterceptor.INSTANCE, ExtAuthzServerInterceptor.INSTANCE, |
| 108 | + HeaderMutationFilter.INSTANCE, HeaderMutator.create()); |
| 109 | + } |
| 110 | + |
| 111 | + @Override |
| 112 | + public ConfigOrError<ExtAuthzFilterConfig> parseFilterConfig(Message rawProtoMessage) { |
| 113 | + ExtAuthz extAuthzProto; |
| 114 | + if (!(rawProtoMessage instanceof Any)) { |
| 115 | + return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass()); |
| 116 | + } |
| 117 | + Any anyMessage = (Any) rawProtoMessage; |
| 118 | + try { |
| 119 | + extAuthzProto = anyMessage.unpack(ExtAuthz.class); |
| 120 | + return ConfigOrError.fromConfig(ExtAuthzFilterConfig.fromProto(extAuthzProto)); |
| 121 | + } catch (InvalidProtocolBufferException | ExtAuthzParseException e) { |
| 122 | + return ConfigOrError.fromError("Invalid proto: " + e); |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + @Override |
| 127 | + public ConfigOrError<ExtAuthzFilterConfigOverride> parseFilterConfigOverride( |
| 128 | + Message rawProtoMessage) { |
| 129 | + if (!(rawProtoMessage instanceof Any)) { |
| 130 | + return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass()); |
| 131 | + } |
| 132 | + return ConfigOrError.fromConfig(new ExtAuthzFilterConfigOverride()); |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + private final StubManager stubManager; |
| 137 | + private final ThreadSafeRandom random; |
| 138 | + private final BufferingAuthzClientCall.Factory bufferingAuthzClientCallFactory; |
| 139 | + private final ExtAuthzCertificateProvider certificateProvider; |
| 140 | + private final CheckRequestBuilder.Factory checkRequestBuilderFactory; |
| 141 | + private final CheckResponseHandler.Factory checkResponseHandlerFactory; |
| 142 | + private final ExtAuthzClientInterceptor.Factory extAuthzClientInterceptorFactory; |
| 143 | + private final ExtAuthzServerInterceptor.Factory extAuthzServerInterceptorFactory; |
| 144 | + private final HeaderMutationFilter.Factory headerMutationFilterFactory; |
| 145 | + private final HeaderMutator headerMutator; |
| 146 | + |
| 147 | + |
| 148 | + ExtAuthzFilter(StubManager stubManager, ThreadSafeRandom random, |
| 149 | + BufferingAuthzClientCall.Factory bufferingAuthzClientCallFactory, |
| 150 | + ExtAuthzCertificateProvider certificateProvider, |
| 151 | + CheckRequestBuilder.Factory checkRequestBuilderFactory, |
| 152 | + CheckResponseHandler.Factory checkResponseHandlerFactory, |
| 153 | + ExtAuthzClientInterceptor.Factory extAuthzClientInterceptorFactory, |
| 154 | + ExtAuthzServerInterceptor.Factory extAuthzServerInterceptorFactory, |
| 155 | + HeaderMutationFilter.Factory headerMutationFilterFactory, HeaderMutator headerMutator) { |
| 156 | + this.stubManager = stubManager; |
| 157 | + this.random = random; |
| 158 | + this.bufferingAuthzClientCallFactory = bufferingAuthzClientCallFactory; |
| 159 | + this.certificateProvider = certificateProvider; |
| 160 | + this.checkRequestBuilderFactory = checkRequestBuilderFactory; |
| 161 | + this.checkResponseHandlerFactory = checkResponseHandlerFactory; |
| 162 | + this.extAuthzClientInterceptorFactory = extAuthzClientInterceptorFactory; |
| 163 | + this.extAuthzServerInterceptorFactory = extAuthzServerInterceptorFactory; |
| 164 | + this.headerMutationFilterFactory = headerMutationFilterFactory; |
| 165 | + this.headerMutator = headerMutator; |
| 166 | + } |
| 167 | + |
| 168 | + @Nullable |
| 169 | + @Override |
| 170 | + public ClientInterceptor buildClientInterceptor(FilterConfig config, |
| 171 | + @Nullable FilterConfig overrideConfig, ScheduledExecutorService scheduler) { |
| 172 | + if (overrideConfig != null) { |
| 173 | + return null; |
| 174 | + } |
| 175 | + if (!(config instanceof ExtAuthzFilterConfig)) { |
| 176 | + return null; |
| 177 | + } |
| 178 | + ExtAuthzFilterConfig extAuthzFilterConfig = (ExtAuthzFilterConfig) config; |
| 179 | + AuthorizationGrpc.AuthorizationStub stub = |
| 180 | + stubManager.getStub(extAuthzFilterConfig.extAuthzConfig()); |
| 181 | + ExtAuthzConfig extAuthzConfig = extAuthzFilterConfig.extAuthzConfig(); |
| 182 | + return extAuthzClientInterceptorFactory.create(extAuthzConfig, stub, |
| 183 | + random, bufferingAuthzClientCallFactory, |
| 184 | + checkRequestBuilderFactory.create(extAuthzConfig, certificateProvider), |
| 185 | + checkResponseHandlerFactory.create(headerMutator, |
| 186 | + headerMutationFilterFactory.create(extAuthzConfig.decoderHeaderMutationRules()), |
| 187 | + extAuthzConfig), |
| 188 | + headerMutator); |
| 189 | + } |
| 190 | + |
| 191 | + @Nullable |
| 192 | + @Override |
| 193 | + public ServerInterceptor buildServerInterceptor(FilterConfig config, |
| 194 | + @Nullable FilterConfig overrideConfig) { |
| 195 | + if (overrideConfig != null) { |
| 196 | + return null; |
| 197 | + } |
| 198 | + if (!(config instanceof ExtAuthzFilterConfig)) { |
| 199 | + return null; |
| 200 | + } |
| 201 | + ExtAuthzFilterConfig extAuthzFilterConfig = (ExtAuthzFilterConfig) config; |
| 202 | + AuthorizationGrpc.AuthorizationStub stub = |
| 203 | + stubManager.getStub(extAuthzFilterConfig.extAuthzConfig()); |
| 204 | + ExtAuthzConfig extAuthzConfig = extAuthzFilterConfig.extAuthzConfig(); |
| 205 | + return extAuthzServerInterceptorFactory.create(extAuthzConfig, stub, random, |
| 206 | + checkRequestBuilderFactory.create(extAuthzConfig, certificateProvider), |
| 207 | + checkResponseHandlerFactory.create(headerMutator, |
| 208 | + headerMutationFilterFactory.create(extAuthzConfig.decoderHeaderMutationRules()), |
| 209 | + extAuthzConfig), |
| 210 | + headerMutator); |
| 211 | + } |
| 212 | + |
| 213 | + @Override |
| 214 | + public void close() { |
| 215 | + stubManager.close(); |
| 216 | + } |
| 217 | +} |
0 commit comments