Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

accesslogs: add CEL-based extension filter #18363

Merged
merged 20 commits into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/api/ @envoyproxy/api-shepherds
# access loggers
/*/extensions/access_loggers/common @auni53 @zuercher
/*/extensions/access_loggers/filters/cel @dio @douglas-reid
/*/extensions/access_loggers/open_telemetry @itamarkam @yanavlasov
/*/extensions/access_loggers/stream @mattklein123 @davinci26
# compression extensions
Expand Down
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ proto_library(
"//envoy/data/dns/v3:pkg",
"//envoy/data/tap/v3:pkg",
"//envoy/extensions/access_loggers/file/v3:pkg",
"//envoy/extensions/access_loggers/filters/cel/v3:pkg",
"//envoy/extensions/access_loggers/grpc/v3:pkg",
"//envoy/extensions/access_loggers/open_telemetry/v3:pkg",
"//envoy/extensions/access_loggers/stream/v3:pkg",
Expand Down
1 change: 1 addition & 0 deletions api/envoy/config/accesslog/v3/accesslog.proto
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ message AccessLogFilter {
GrpcStatusFilter grpc_status_filter = 10;

// Extension filter.
// [#extension-category: envoy.access_loggers.extension_filters]
ExtensionFilter extension_filter = 11;

// Metadata Filter
Expand Down
9 changes: 9 additions & 0 deletions api/envoy/extensions/access_loggers/filters/cel/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"],
)
26 changes: 26 additions & 0 deletions api/envoy/extensions/access_loggers/filters/cel/v3/cel.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
syntax = "proto3";

package envoy.extensions.access_loggers.filters.cel.v3;

import "udpa/annotations/status.proto";

option java_package = "io.envoyproxy.envoy.extensions.access_loggers.filters.cel.v3";
option java_outer_classname = "CelProto";
option java_multiple_files = true;
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: ExpressionFilter]
// [#extension: envoy.access_loggers.extension_filters.cel]

// ExpressionFilter is an access logging filter that evaluates configured
// symbolic Common Expression Language expressions to inform the decision
// to generate an access log.
message ExpressionFilter {
// Expression that, when evaluated, will be used to filter access logs.
// Expressions are based on the set of Envoy :ref:`attributes <arch_overview_attributes>`.
// The provided expression must evaluate to true for logging (expression errors are considered false).
// Examples:
// - `response.code >= 400`
// - `(connection.mtls && request.headers['x-log-mtls'] == 'true') || request.url_path.contains('v1beta3')`
string expression = 1;
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ proto_library(
"//envoy/data/dns/v3:pkg",
"//envoy/data/tap/v3:pkg",
"//envoy/extensions/access_loggers/file/v3:pkg",
"//envoy/extensions/access_loggers/filters/cel/v3:pkg",
"//envoy/extensions/access_loggers/grpc/v3:pkg",
"//envoy/extensions/access_loggers/open_telemetry/v3:pkg",
"//envoy/extensions/access_loggers/stream/v3:pkg",
Expand Down
4 changes: 4 additions & 0 deletions bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,7 @@ REPOSITORY_LOCATIONS_SPEC = dict(
urls = ["https://github.com/google/cel-cpp/archive/{version}.tar.gz"],
use_category = ["dataplane_ext"],
extensions = [
"envoy.access_loggers.extension_filters.cel",
"envoy.access_loggers.wasm",
"envoy.bootstrap.wasm",
"envoy.rate_limit_descriptors.expr",
Expand All @@ -882,6 +883,7 @@ REPOSITORY_LOCATIONS_SPEC = dict(
urls = ["https://github.com/google/flatbuffers/archive/v{version}.tar.gz"],
use_category = ["dataplane_ext"],
extensions = [
"envoy.access_loggers.extension_filters.cel",
"envoy.access_loggers.wasm",
"envoy.bootstrap.wasm",
"envoy.rate_limit_descriptors.expr",
Expand Down Expand Up @@ -1078,6 +1080,7 @@ REPOSITORY_LOCATIONS_SPEC = dict(
# ANTLR has a runtime component, so is not purely build.
use_category = ["dataplane_ext"],
extensions = [
"envoy.access_loggers.extension_filters.cel",
"envoy.access_loggers.wasm",
"envoy.bootstrap.wasm",
"envoy.rate_limit_descriptors.expr",
Expand All @@ -1098,6 +1101,7 @@ REPOSITORY_LOCATIONS_SPEC = dict(
urls = ["https://github.com/antlr/antlr4/archive/{version}.tar.gz"],
use_category = ["dataplane_ext"],
extensions = [
"envoy.access_loggers.extension_filters.cel",
"envoy.access_loggers.wasm",
"envoy.bootstrap.wasm",
"envoy.rate_limit_descriptors.expr",
Expand Down
1 change: 1 addition & 0 deletions docs/root/api-v3/config/accesslog/accesslog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Access loggers

v3/*
../../extensions/access_loggers/*/v3/*
../../extensions/access_loggers/filters/*/v3/*
8 changes: 8 additions & 0 deletions docs/root/api-v3/config/accesslog/filters.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Extension Filters
=================

.. toctree::
:glob:
:maxdepth: 2

filters/filters
8 changes: 8 additions & 0 deletions docs/root/api-v3/config/accesslog/filters/filters.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Extension Filters
=================

.. toctree::
:glob:
:maxdepth: 2

../../../extensions/access_loggers/filters/*/v3/*
1 change: 1 addition & 0 deletions docs/root/api-v3/config/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Extensions

filter/filter
accesslog/accesslog
accesslog/filters
rbac/rbac
health_checker/health_checker
transport_socket/transport_socket
Expand Down
2 changes: 2 additions & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Removed Config or Runtime
New Features
------------
* access log: added :ref:`grpc_stream_retry_policy <envoy_v3_api_field_extensions.access_loggers.grpc.v3.CommonGrpcAccessLogConfig.grpc_stream_retry_policy>` to the gRPC logger to reconnect when a connection fails to be established.
* access_log: added :ref:`METADATA<envoy_v3_api_msg_extensions.formatter.metadata.v3.Metadata>` token to handle all types of metadata (DYNAMIC, CLUSTER, ROUTE).
* access_log: added a CEL extension filter to enable filtering of access logs based on Envoy attribute expressions.
* access_log: added new access_log command operator ``%UPSTREAM_REQUEST_ATTEMPT_COUNT%`` to retrieve the number of times given request got attempted upstream.
* access_log: added new access_log command operator ``%VIRTUAL_CLUSTER_NAME%`` to retrieve the matched Virtual Cluster name.
* api: added support for *xds.type.v3.TypedStruct* in addition to the now-deprecated *udpa.type.v1.TypedStruct* proto message, which is a wrapper proto used to encode typed JSON data in a *google.protobuf.Any* field.
Expand Down
63 changes: 63 additions & 0 deletions source/extensions/access_loggers/filters/cel/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_extension",
"envoy_extension_package",
)

licenses(["notice"]) # Apache 2

envoy_extension_package()

envoy_cc_extension(
name = "cel_lib",
srcs = ["cel.cc"],
hdrs = ["cel.h"],
extra_visibility = [
"//test:__subpackages__",
],
deps = [
"//envoy/access_log:access_log_interface",
"//envoy/http:header_map_interface",
"//envoy/stream_info:stream_info_interface",
"//source/common/access_log:access_log_lib",
"//source/common/config:utility_lib",
"//source/common/protobuf",
"//source/common/protobuf:utility_lib",
"//source/extensions/filters/common/expr:evaluator_lib",
],
)

envoy_cc_extension(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
copts = select({
"//bazel:windows_x86_64": [], # TODO: fix the windows ANTLR build
"//conditions:default": [
"-DUSE_CEL_PARSER",
],
}),
extra_visibility = [
"//test:__subpackages__",
],
deps = [
":cel_lib",
"//envoy/access_log:access_log_interface",
"//envoy/http:header_map_interface",
"//envoy/registry",
"//envoy/stream_info:stream_info_interface",
"//source/common/access_log:access_log_lib",
"//source/common/config:utility_lib",
"//source/common/protobuf",
"//source/common/protobuf:utility_lib",
"//source/extensions/filters/common/expr:evaluator_lib",
"@envoy_api//envoy/extensions/access_loggers/filters/cel/v3:pkg_cc_proto",
] + select(
{
"//bazel:windows_x86_64": [],
"//conditions:default": [
"@com_google_cel_cpp//parser",
],
},
),
)
35 changes: 35 additions & 0 deletions source/extensions/access_loggers/filters/cel/cel.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "source/extensions/access_loggers/filters/cel/cel.h"

namespace Envoy {
namespace Extensions {
namespace AccessLoggers {
namespace Filters {
namespace CEL {

namespace Expr = Envoy::Extensions::Filters::Common::Expr;

CELAccessLogExtensionFilter::CELAccessLogExtensionFilter(
Expr::Builder& builder, const google::api::expr::v1alpha1::Expr& input_expr)
: parsed_expr_(input_expr) {
compiled_expr_ = Expr::createExpression(builder, parsed_expr_);
}

bool CELAccessLogExtensionFilter::evaluate(
const StreamInfo::StreamInfo& stream_info, const Http::RequestHeaderMap& request_headers,
const Http::ResponseHeaderMap& response_headers,
const Http::ResponseTrailerMap& response_trailers) const {
Protobuf::Arena arena;
auto eval_status = Expr::evaluate(*compiled_expr_, arena, stream_info, &request_headers,
&response_headers, &response_trailers);
if (!eval_status.has_value() || eval_status.value().IsError()) {
return false;
}
auto result = eval_status.value();
return result.IsBool() ? result.BoolOrDie() : false;
}

} // namespace CEL
} // namespace Filters
} // namespace AccessLoggers
} // namespace Extensions
} // namespace Envoy
36 changes: 36 additions & 0 deletions source/extensions/access_loggers/filters/cel/cel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "envoy/access_log/access_log.h"
#include "envoy/http/header_map.h"
#include "envoy/stream_info/stream_info.h"

#include "source/common/access_log/access_log_impl.h"
#include "source/common/config/utility.h"
#include "source/common/protobuf/message_validator_impl.h"
#include "source/common/protobuf/protobuf.h"
#include "source/common/protobuf/utility.h"
#include "source/extensions/filters/common/expr/evaluator.h"

namespace Envoy {
namespace Extensions {
namespace AccessLoggers {
namespace Filters {
namespace CEL {

class CELAccessLogExtensionFilter : public AccessLog::Filter {
public:
CELAccessLogExtensionFilter(Extensions::Filters::Common::Expr::Builder&,
const google::api::expr::v1alpha1::Expr&);

bool evaluate(const StreamInfo::StreamInfo& info, const Http::RequestHeaderMap& request_headers,
const Http::ResponseHeaderMap& response_headers,
const Http::ResponseTrailerMap& response_trailers) const override;

private:
const google::api::expr::v1alpha1::Expr parsed_expr_;
Extensions::Filters::Common::Expr::ExpressionPtr compiled_expr_;
};

} // namespace CEL
} // namespace Filters
} // namespace AccessLoggers
} // namespace Extensions
} // namespace Envoy
65 changes: 65 additions & 0 deletions source/extensions/access_loggers/filters/cel/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "source/extensions/access_loggers/filters/cel/config.h"

#include "envoy/extensions/access_loggers/filters/cel/v3/cel.pb.h"

#include "source/extensions/access_loggers/filters/cel/cel.h"

#if defined(USE_CEL_PARSER)
#include "parser/parser.h"
#endif

namespace Envoy {
namespace Extensions {
namespace AccessLoggers {
namespace Filters {
namespace CEL {

Envoy::AccessLog::FilterPtr CELAccessLogExtensionFilterFactory::createFilter(
const envoy::config::accesslog::v3::ExtensionFilter& config, Runtime::Loader&,
Random::RandomGenerator&) {

// TODO(douglas-reid): use factory_context validation. likely needs update to
// createFilter signature to pass in validation visitor.
auto factory_config = Config::Utility::translateToFactoryConfig(
config, Envoy::ProtobufMessage::getNullValidationVisitor(), *this);

#if defined(USE_CEL_PARSER)
envoy::extensions::access_loggers::filters::cel::v3::ExpressionFilter cel_config =
*dynamic_cast<const envoy::extensions::access_loggers::filters::cel::v3::ExpressionFilter*>(
factory_config.get());

auto parse_status = google::api::expr::parser::Parse(cel_config.expression());
if (!parse_status.ok()) {
throw EnvoyException("Not able to parse filter expression: " +
parse_status.status().ToString());
}

return std::make_unique<CELAccessLogExtensionFilter>(getOrCreateBuilder(),
parse_status.value().expr());
#else
throw EnvoyException("CEL is not available for use in this environment.");
#endif
}

ProtobufTypes::MessagePtr CELAccessLogExtensionFilterFactory::createEmptyConfigProto() {
return std::make_unique<envoy::extensions::access_loggers::filters::cel::v3::ExpressionFilter>();
}

Extensions::Filters::Common::Expr::Builder&
CELAccessLogExtensionFilterFactory::getOrCreateBuilder() {
if (expr_builder_ == nullptr) {
expr_builder_ = Extensions::Filters::Common::Expr::createBuilder(nullptr);
}
return *expr_builder_;
}

/**
* Static registration for the CELAccessLogExtensionFilter. @see RegisterFactory.
*/
REGISTER_FACTORY(CELAccessLogExtensionFilterFactory, Envoy::AccessLog::ExtensionFilterFactory);

} // namespace CEL
} // namespace Filters
} // namespace AccessLoggers
} // namespace Extensions
} // namespace Envoy
36 changes: 36 additions & 0 deletions source/extensions/access_loggers/filters/cel/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "envoy/access_log/access_log.h"
#include "envoy/http/header_map.h"
#include "envoy/registry/registry.h"
#include "envoy/stream_info/stream_info.h"

#include "source/common/access_log/access_log_impl.h"
#include "source/common/config/utility.h"
#include "source/common/protobuf/message_validator_impl.h"
#include "source/common/protobuf/protobuf.h"
#include "source/common/protobuf/utility.h"
#include "source/extensions/filters/common/expr/evaluator.h"

namespace Envoy {
namespace Extensions {
namespace AccessLoggers {
namespace Filters {
namespace CEL {

class CELAccessLogExtensionFilterFactory : public Envoy::AccessLog::ExtensionFilterFactory {
public:
Envoy::AccessLog::FilterPtr
createFilter(const envoy::config::accesslog::v3::ExtensionFilter& config, Runtime::Loader&,
Random::RandomGenerator&) override;
ProtobufTypes::MessagePtr createEmptyConfigProto() override;
std::string name() const override { return "envoy.access_loggers.extension_filters.cel"; }

private:
Extensions::Filters::Common::Expr::Builder& getOrCreateBuilder();
Extensions::Filters::Common::Expr::BuilderPtr expr_builder_;
};

} // namespace CEL
} // namespace Filters
} // namespace AccessLoggers
} // namespace Extensions
} // namespace Envoy
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ EXTENSIONS = {
#

"envoy.access_loggers.file": "//source/extensions/access_loggers/file:config",
"envoy.access_loggers.extension_filters.cel": "//source/extensions/access_loggers/filters/cel:config",
"envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config",
"envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config",
"envoy.access_loggers.open_telemetry": "//source/extensions/access_loggers/open_telemetry:config",
Expand Down
Loading