Line data Source code
1 : #include "source/common/access_log/access_log_impl.h"
2 :
3 : #include <cstdint>
4 : #include <string>
5 :
6 : #include "envoy/common/time.h"
7 : #include "envoy/config/accesslog/v3/accesslog.pb.h"
8 : #include "envoy/config/accesslog/v3/accesslog.pb.validate.h"
9 : #include "envoy/filesystem/filesystem.h"
10 : #include "envoy/http/header_map.h"
11 : #include "envoy/runtime/runtime.h"
12 : #include "envoy/upstream/upstream.h"
13 :
14 : #include "source/common/common/assert.h"
15 : #include "source/common/common/utility.h"
16 : #include "source/common/config/metadata.h"
17 : #include "source/common/config/utility.h"
18 : #include "source/common/http/header_map_impl.h"
19 : #include "source/common/http/header_utility.h"
20 : #include "source/common/http/headers.h"
21 : #include "source/common/http/utility.h"
22 : #include "source/common/protobuf/utility.h"
23 : #include "source/common/stream_info/utility.h"
24 : #include "source/common/tracing/http_tracer_impl.h"
25 :
26 : #include "absl/types/optional.h"
27 :
28 : namespace Envoy {
29 : namespace AccessLog {
30 :
31 : ComparisonFilter::ComparisonFilter(const envoy::config::accesslog::v3::ComparisonFilter& config,
32 : Runtime::Loader& runtime)
33 0 : : config_(config), runtime_(runtime) {}
34 :
35 0 : bool ComparisonFilter::compareAgainstValue(uint64_t lhs) const {
36 0 : uint64_t value = config_.value().default_value();
37 :
38 0 : if (!config_.value().runtime_key().empty()) {
39 0 : value = runtime_.snapshot().getInteger(config_.value().runtime_key(), value);
40 0 : }
41 :
42 0 : switch (config_.op()) {
43 0 : PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
44 0 : case envoy::config::accesslog::v3::ComparisonFilter::GE:
45 0 : return lhs >= value;
46 0 : case envoy::config::accesslog::v3::ComparisonFilter::EQ:
47 0 : return lhs == value;
48 0 : case envoy::config::accesslog::v3::ComparisonFilter::LE:
49 0 : return lhs <= value;
50 0 : }
51 0 : IS_ENVOY_BUG("unexpected comparison op enum");
52 0 : return false;
53 0 : }
54 :
55 : FilterPtr FilterFactory::fromProto(const envoy::config::accesslog::v3::AccessLogFilter& config,
56 70 : Server::Configuration::FactoryContext& context) {
57 70 : Runtime::Loader& runtime = context.serverFactoryContext().runtime();
58 70 : Random::RandomGenerator& random = context.serverFactoryContext().api().randomGenerator();
59 70 : ProtobufMessage::ValidationVisitor& validation_visitor = context.messageValidationVisitor();
60 70 : switch (config.filter_specifier_case()) {
61 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kStatusCodeFilter:
62 0 : return FilterPtr{new StatusCodeFilter(config.status_code_filter(), runtime)};
63 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kDurationFilter:
64 0 : return FilterPtr{new DurationFilter(config.duration_filter(), runtime)};
65 70 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kNotHealthCheckFilter:
66 70 : return FilterPtr{new NotHealthCheckFilter()};
67 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kTraceableFilter:
68 0 : return FilterPtr{new TraceableRequestFilter()};
69 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kRuntimeFilter:
70 0 : return FilterPtr{new RuntimeFilter(config.runtime_filter(), runtime, random)};
71 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kAndFilter:
72 0 : return FilterPtr{new AndFilter(config.and_filter(), context)};
73 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kOrFilter:
74 0 : return FilterPtr{new OrFilter(config.or_filter(), context)};
75 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kHeaderFilter:
76 0 : return FilterPtr{new HeaderFilter(config.header_filter())};
77 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kResponseFlagFilter:
78 0 : MessageUtil::validate(config, validation_visitor);
79 0 : return FilterPtr{new ResponseFlagFilter(config.response_flag_filter())};
80 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kGrpcStatusFilter:
81 0 : MessageUtil::validate(config, validation_visitor);
82 0 : return FilterPtr{new GrpcStatusFilter(config.grpc_status_filter())};
83 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kMetadataFilter:
84 0 : return FilterPtr{new MetadataFilter(config.metadata_filter())};
85 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kLogTypeFilter:
86 0 : return FilterPtr{new LogTypeFilter(config.log_type_filter())};
87 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kExtensionFilter:
88 0 : MessageUtil::validate(config, validation_visitor);
89 0 : {
90 0 : auto& factory =
91 0 : Config::Utility::getAndCheckFactory<ExtensionFilterFactory>(config.extension_filter());
92 0 : return factory.createFilter(config.extension_filter(), context);
93 0 : }
94 0 : case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::FILTER_SPECIFIER_NOT_SET:
95 0 : PANIC_DUE_TO_PROTO_UNSET;
96 70 : }
97 0 : IS_ENVOY_BUG("unexpected filter specifier value");
98 0 : return nullptr;
99 70 : }
100 :
101 : bool TraceableRequestFilter::evaluate(const Formatter::HttpFormatterContext&,
102 0 : const StreamInfo::StreamInfo& info) const {
103 0 : const Tracing::Decision decision = Tracing::TracerUtility::shouldTraceRequest(info);
104 0 : return decision.traced && decision.reason == Tracing::Reason::ServiceForced;
105 0 : }
106 :
107 : bool StatusCodeFilter::evaluate(const Formatter::HttpFormatterContext&,
108 0 : const StreamInfo::StreamInfo& info) const {
109 0 : if (!info.responseCode()) {
110 0 : return compareAgainstValue(0ULL);
111 0 : }
112 :
113 0 : return compareAgainstValue(info.responseCode().value());
114 0 : }
115 :
116 : bool DurationFilter::evaluate(const Formatter::HttpFormatterContext&,
117 0 : const StreamInfo::StreamInfo& info) const {
118 0 : absl::optional<std::chrono::nanoseconds> duration = info.currentDuration();
119 0 : if (!duration.has_value()) {
120 0 : return false;
121 0 : }
122 :
123 0 : return compareAgainstValue(
124 0 : std::chrono::duration_cast<std::chrono::milliseconds>(duration.value()).count());
125 0 : }
126 :
127 : RuntimeFilter::RuntimeFilter(const envoy::config::accesslog::v3::RuntimeFilter& config,
128 : Runtime::Loader& runtime, Random::RandomGenerator& random)
129 : : runtime_(runtime), random_(random), runtime_key_(config.runtime_key()),
130 : percent_(config.percent_sampled()),
131 0 : use_independent_randomness_(config.use_independent_randomness()) {}
132 :
133 : bool RuntimeFilter::evaluate(const Formatter::HttpFormatterContext&,
134 0 : const StreamInfo::StreamInfo& stream_info) const {
135 : // This code is verbose to avoid preallocating a random number that is not needed.
136 0 : uint64_t random_value;
137 0 : if (use_independent_randomness_) {
138 0 : random_value = random_.random();
139 0 : } else if (!stream_info.getStreamIdProvider().has_value()) {
140 0 : random_value = random_.random();
141 0 : } else {
142 0 : const auto rid_to_integer = stream_info.getStreamIdProvider()->toInteger();
143 0 : if (!rid_to_integer.has_value()) {
144 0 : random_value = random_.random();
145 0 : } else {
146 0 : random_value =
147 0 : rid_to_integer.value() %
148 0 : ProtobufPercentHelper::fractionalPercentDenominatorToInt(percent_.denominator());
149 0 : }
150 0 : }
151 :
152 0 : return runtime_.snapshot().featureEnabled(
153 0 : runtime_key_, percent_.numerator(), random_value,
154 0 : ProtobufPercentHelper::fractionalPercentDenominatorToInt(percent_.denominator()));
155 0 : }
156 :
157 : OperatorFilter::OperatorFilter(
158 : const Protobuf::RepeatedPtrField<envoy::config::accesslog::v3::AccessLogFilter>& configs,
159 0 : Server::Configuration::FactoryContext& context) {
160 0 : for (const auto& config : configs) {
161 0 : auto filter = FilterFactory::fromProto(config, context);
162 0 : if (filter != nullptr) {
163 0 : filters_.emplace_back(std::move(filter));
164 0 : }
165 0 : }
166 0 : }
167 :
168 : OrFilter::OrFilter(const envoy::config::accesslog::v3::OrFilter& config,
169 : Server::Configuration::FactoryContext& context)
170 0 : : OperatorFilter(config.filters(), context) {}
171 :
172 : AndFilter::AndFilter(const envoy::config::accesslog::v3::AndFilter& config,
173 : Server::Configuration::FactoryContext& context)
174 0 : : OperatorFilter(config.filters(), context) {}
175 :
176 : bool OrFilter::evaluate(const Formatter::HttpFormatterContext& context,
177 0 : const StreamInfo::StreamInfo& info) const {
178 0 : bool result = false;
179 0 : for (auto& filter : filters_) {
180 0 : result |= filter->evaluate(context, info);
181 :
182 0 : if (result) {
183 0 : break;
184 0 : }
185 0 : }
186 :
187 0 : return result;
188 0 : }
189 :
190 : bool AndFilter::evaluate(const Formatter::HttpFormatterContext& context,
191 0 : const StreamInfo::StreamInfo& info) const {
192 0 : bool result = true;
193 0 : for (auto& filter : filters_) {
194 0 : result &= filter->evaluate(context, info);
195 :
196 0 : if (!result) {
197 0 : break;
198 0 : }
199 0 : }
200 :
201 0 : return result;
202 0 : }
203 :
204 : bool NotHealthCheckFilter::evaluate(const Formatter::HttpFormatterContext&,
205 514 : const StreamInfo::StreamInfo& info) const {
206 514 : return !info.healthCheck();
207 514 : }
208 :
209 : HeaderFilter::HeaderFilter(const envoy::config::accesslog::v3::HeaderFilter& config)
210 0 : : header_data_(std::make_unique<Http::HeaderUtility::HeaderData>(config.header())) {}
211 :
212 : bool HeaderFilter::evaluate(const Formatter::HttpFormatterContext& context,
213 0 : const StreamInfo::StreamInfo&) const {
214 0 : return Http::HeaderUtility::matchHeaders(context.requestHeaders(), *header_data_);
215 0 : }
216 :
217 : ResponseFlagFilter::ResponseFlagFilter(
218 0 : const envoy::config::accesslog::v3::ResponseFlagFilter& config) {
219 0 : for (int i = 0; i < config.flags_size(); i++) {
220 0 : absl::optional<StreamInfo::ResponseFlag> response_flag =
221 0 : StreamInfo::ResponseFlagUtils::toResponseFlag(config.flags(i));
222 : // The config has been validated. Therefore, every flag in the config will have a mapping.
223 0 : ASSERT(response_flag.has_value());
224 0 : configured_flags_ |= response_flag.value();
225 0 : }
226 0 : }
227 :
228 : bool ResponseFlagFilter::evaluate(const Formatter::HttpFormatterContext&,
229 0 : const StreamInfo::StreamInfo& info) const {
230 0 : if (configured_flags_ != 0) {
231 0 : return info.intersectResponseFlags(configured_flags_);
232 0 : }
233 0 : return info.hasAnyResponseFlag();
234 0 : }
235 :
236 0 : GrpcStatusFilter::GrpcStatusFilter(const envoy::config::accesslog::v3::GrpcStatusFilter& config) {
237 0 : for (int i = 0; i < config.statuses_size(); i++) {
238 0 : statuses_.insert(protoToGrpcStatus(config.statuses(i)));
239 0 : }
240 :
241 0 : exclude_ = config.exclude();
242 0 : }
243 :
244 : bool GrpcStatusFilter::evaluate(const Formatter::HttpFormatterContext& context,
245 0 : const StreamInfo::StreamInfo& info) const {
246 :
247 0 : Grpc::Status::GrpcStatus status = Grpc::Status::WellKnownGrpcStatus::Unknown;
248 0 : const auto& optional_status =
249 0 : Grpc::Common::getGrpcStatus(context.responseTrailers(), context.responseHeaders(), info);
250 0 : if (optional_status.has_value()) {
251 0 : status = optional_status.value();
252 0 : }
253 :
254 0 : const bool found = statuses_.find(status) != statuses_.end();
255 0 : return exclude_ ? !found : found;
256 0 : }
257 :
258 : Grpc::Status::GrpcStatus GrpcStatusFilter::protoToGrpcStatus(
259 0 : envoy::config::accesslog::v3::GrpcStatusFilter::Status status) const {
260 0 : return static_cast<Grpc::Status::GrpcStatus>(status);
261 0 : }
262 :
263 0 : LogTypeFilter::LogTypeFilter(const envoy::config::accesslog::v3::LogTypeFilter& config) {
264 0 : for (auto type_as_int : config.types()) {
265 0 : types_.insert(static_cast<AccessLogType>(type_as_int));
266 0 : }
267 :
268 0 : exclude_ = config.exclude();
269 0 : }
270 :
271 : bool LogTypeFilter::evaluate(const Formatter::HttpFormatterContext& context,
272 0 : const StreamInfo::StreamInfo&) const {
273 0 : const bool found = types_.contains(context.accessLogType());
274 0 : return exclude_ ? !found : found;
275 0 : }
276 :
277 : MetadataFilter::MetadataFilter(const envoy::config::accesslog::v3::MetadataFilter& filter_config)
278 : : default_match_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(filter_config, match_if_key_not_found, true)),
279 0 : filter_(filter_config.matcher().filter()) {
280 :
281 0 : if (filter_config.has_matcher()) {
282 0 : auto& matcher_config = filter_config.matcher();
283 :
284 0 : for (const auto& seg : matcher_config.path()) {
285 0 : path_.push_back(seg.key());
286 0 : }
287 :
288 : // Matches if the value equals the configured 'MetadataMatcher' value.
289 0 : const auto& val = matcher_config.value();
290 0 : value_matcher_ = Matchers::ValueMatcher::create(val);
291 0 : }
292 :
293 : // Matches if the value is present in dynamic metadata
294 0 : auto present_val = envoy::type::matcher::v3::ValueMatcher();
295 0 : present_val.set_present_match(true);
296 0 : present_matcher_ = Matchers::ValueMatcher::create(present_val);
297 0 : }
298 :
299 : bool MetadataFilter::evaluate(const Formatter::HttpFormatterContext&,
300 0 : const StreamInfo::StreamInfo& info) const {
301 0 : const auto& value =
302 0 : Envoy::Config::Metadata::metadataValue(&info.dynamicMetadata(), filter_, path_);
303 : // If the key corresponds to a set value in dynamic metadata, return true if the value matches the
304 : // the configured 'MetadataMatcher' value and false otherwise
305 0 : if (present_matcher_->match(value)) {
306 0 : return value_matcher_ && value_matcher_->match(value);
307 0 : }
308 :
309 : // If the key does not correspond to a set value in dynamic metadata, return true if
310 : // 'match_if_key_not_found' is set to true and false otherwise
311 0 : return default_match_;
312 0 : }
313 :
314 : InstanceSharedPtr AccessLogFactory::fromProto(const envoy::config::accesslog::v3::AccessLog& config,
315 168 : Server::Configuration::FactoryContext& context) {
316 168 : FilterPtr filter;
317 168 : if (config.has_filter()) {
318 70 : filter = FilterFactory::fromProto(config.filter(), context);
319 70 : }
320 :
321 168 : auto& factory = Config::Utility::getAndCheckFactory<AccessLog::AccessLogInstanceFactory>(config);
322 168 : ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(
323 168 : config, context.messageValidationVisitor(), factory);
324 :
325 168 : return factory.createAccessLogInstance(*message, std::move(filter), context);
326 168 : }
327 :
328 : } // namespace AccessLog
329 : } // namespace Envoy
|