-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
filter: add conditions to access control filter (#7716)
Introduces a generic expression-based admission filter using https://github.com/google/cel-cpp. This is a follow-up to discussion in #6751. The advantage of this approach is: 1. Un-opinionated about the policy structure since the only config is an expression. This is friendly towards control planes which can bear the complexity of translation, analysis, and evolution of policies. 2. Multi-language, CEL supports go, java, and c++ runtimes. 3. Inter-operability with other filters using request `metadata`. Companion filters can populate metadata about requests and resources that affect policy decisions. 4. Generic utility, it can be used for custom metric labels, access log entries, etc. The dis-advantage of this approach is that its performance is lower than domain-optimized interpreters. On a fair example, the interpreter evaluates in around 1ms (see https://github.com/google/cel-cpp/blob/master/eval/tests/benchmark_test.cc#L591) vs ~150ns for hand-written C++ native code. There is space for improvement (especially if WASM can be used as a compilation target), but ultimately the generic expression form carries a cost. Conditions are added to support RBAC filter for complementing the existing principal/permission model. They add support for the extended checks (e.g. time of query, resource-bound), but add no cost unless used. Description: add expression-based admission filter Risk Level: low Testing: Docs Changes: Release Notes: Signed-off-by: Kuat Yessenov <[email protected]>
- Loading branch information
Showing
29 changed files
with
1,188 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
licenses(["notice"]) # Apache 2 | ||
|
||
load( | ||
"//bazel:envoy_build_system.bzl", | ||
"envoy_cc_library", | ||
"envoy_package", | ||
) | ||
|
||
envoy_package() | ||
|
||
envoy_cc_library( | ||
name = "evaluator_lib", | ||
srcs = ["evaluator.cc"], | ||
hdrs = ["evaluator.h"], | ||
deps = [ | ||
":context_lib", | ||
"//source/common/http:utility_lib", | ||
"//source/common/protobuf", | ||
"@com_google_cel_cpp//eval/public:builtin_func_registrar", | ||
"@com_google_cel_cpp//eval/public:cel_expr_builder_factory", | ||
"@com_google_cel_cpp//eval/public:cel_expression", | ||
"@com_google_cel_cpp//eval/public:cel_value", | ||
], | ||
) | ||
|
||
envoy_cc_library( | ||
name = "context_lib", | ||
srcs = ["context.cc"], | ||
hdrs = ["context.h"], | ||
deps = [ | ||
"//source/common/http:utility_lib", | ||
"@com_google_cel_cpp//eval/public:cel_value", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
#include "extensions/filters/common/expr/context.h" | ||
|
||
#include "absl/strings/numbers.h" | ||
#include "absl/time/time.h" | ||
|
||
namespace Envoy { | ||
namespace Extensions { | ||
namespace Filters { | ||
namespace Common { | ||
namespace Expr { | ||
|
||
namespace { | ||
|
||
absl::optional<CelValue> convertHeaderEntry(const Http::HeaderEntry* header) { | ||
if (header == nullptr) { | ||
return {}; | ||
} | ||
return CelValue::CreateString(header->value().getStringView()); | ||
} | ||
|
||
} // namespace | ||
|
||
absl::optional<CelValue> HeadersWrapper::operator[](CelValue key) const { | ||
if (value_ == nullptr || !key.IsString()) { | ||
return {}; | ||
} | ||
auto out = value_->get(Http::LowerCaseString(std::string(key.StringOrDie().value()))); | ||
return convertHeaderEntry(out); | ||
} | ||
|
||
absl::optional<CelValue> RequestWrapper::operator[](CelValue key) const { | ||
if (!key.IsString()) { | ||
return {}; | ||
} | ||
auto value = key.StringOrDie().value(); | ||
|
||
if (value == Headers) { | ||
return CelValue::CreateMap(&headers_); | ||
} else if (value == Time) { | ||
return CelValue::CreateTimestamp(absl::FromChrono(info_.startTime())); | ||
} else if (value == Size) { | ||
// it is important to make a choice whether to rely on content-length vs stream info | ||
// (which is not available at the time of the request headers) | ||
if (headers_.value_ != nullptr && headers_.value_->ContentLength() != nullptr) { | ||
int64_t length; | ||
if (absl::SimpleAtoi(headers_.value_->ContentLength()->value().getStringView(), &length)) { | ||
return CelValue::CreateInt64(length); | ||
} | ||
} else { | ||
return CelValue::CreateInt64(info_.bytesReceived()); | ||
} | ||
} else if (value == Duration) { | ||
auto duration = info_.requestComplete(); | ||
if (duration.has_value()) { | ||
return CelValue::CreateDuration(absl::FromChrono(duration.value())); | ||
} | ||
} | ||
|
||
if (headers_.value_ != nullptr) { | ||
if (value == Path) { | ||
return convertHeaderEntry(headers_.value_->Path()); | ||
} else if (value == UrlPath) { | ||
absl::string_view path = headers_.value_->Path()->value().getStringView(); | ||
size_t query_offset = path.find('?'); | ||
if (query_offset == absl::string_view::npos) { | ||
return CelValue::CreateString(path); | ||
} | ||
return CelValue::CreateString(path.substr(0, query_offset)); | ||
} else if (value == Host) { | ||
return convertHeaderEntry(headers_.value_->Host()); | ||
} else if (value == Scheme) { | ||
return convertHeaderEntry(headers_.value_->Scheme()); | ||
} else if (value == Method) { | ||
return convertHeaderEntry(headers_.value_->Method()); | ||
} else if (value == Referer) { | ||
return convertHeaderEntry(headers_.value_->Referer()); | ||
} else if (value == ID) { | ||
return convertHeaderEntry(headers_.value_->RequestId()); | ||
} else if (value == UserAgent) { | ||
return convertHeaderEntry(headers_.value_->UserAgent()); | ||
} else if (value == TotalSize) { | ||
return CelValue::CreateInt64(info_.bytesReceived() + headers_.value_->byteSize()); | ||
} | ||
} | ||
return {}; | ||
} | ||
|
||
absl::optional<CelValue> ResponseWrapper::operator[](CelValue key) const { | ||
if (!key.IsString()) { | ||
return {}; | ||
} | ||
auto value = key.StringOrDie().value(); | ||
if (value == Code) { | ||
auto code = info_.responseCode(); | ||
if (code.has_value()) { | ||
return CelValue::CreateInt64(code.value()); | ||
} | ||
} else if (value == Size) { | ||
return CelValue::CreateInt64(info_.bytesSent()); | ||
} else if (value == Headers) { | ||
return CelValue::CreateMap(&headers_); | ||
} else if (value == Trailers) { | ||
return CelValue::CreateMap(&trailers_); | ||
} | ||
return {}; | ||
} | ||
|
||
absl::optional<CelValue> ConnectionWrapper::operator[](CelValue key) const { | ||
if (!key.IsString()) { | ||
return {}; | ||
} | ||
auto value = key.StringOrDie().value(); | ||
if (value == UpstreamAddress) { | ||
auto upstream_host = info_.upstreamHost(); | ||
if (upstream_host != nullptr && upstream_host->address() != nullptr) { | ||
return CelValue::CreateString(upstream_host->address()->asStringView()); | ||
} | ||
} else if (value == UpstreamPort) { | ||
auto upstream_host = info_.upstreamHost(); | ||
if (upstream_host != nullptr && upstream_host->address() != nullptr && | ||
upstream_host->address()->ip() != nullptr) { | ||
return CelValue::CreateInt64(upstream_host->address()->ip()->port()); | ||
} | ||
} else if (value == MTLS) { | ||
return CelValue::CreateBool(info_.downstreamSslConnection() != nullptr && | ||
info_.downstreamSslConnection()->peerCertificatePresented()); | ||
} else if (value == RequestedServerName) { | ||
return CelValue::CreateString(info_.requestedServerName()); | ||
} | ||
|
||
return {}; | ||
} | ||
|
||
absl::optional<CelValue> PeerWrapper::operator[](CelValue key) const { | ||
if (!key.IsString()) { | ||
return {}; | ||
} | ||
auto value = key.StringOrDie().value(); | ||
if (value == Address) { | ||
if (local_) { | ||
return CelValue::CreateString(info_.downstreamLocalAddress()->asStringView()); | ||
} else { | ||
return CelValue::CreateString(info_.downstreamRemoteAddress()->asStringView()); | ||
} | ||
} else if (value == Port) { | ||
if (local_) { | ||
if (info_.downstreamLocalAddress()->ip() != nullptr) { | ||
return CelValue::CreateInt64(info_.downstreamLocalAddress()->ip()->port()); | ||
} | ||
} else { | ||
if (info_.downstreamRemoteAddress()->ip() != nullptr) { | ||
return CelValue::CreateInt64(info_.downstreamRemoteAddress()->ip()->port()); | ||
} | ||
} | ||
} | ||
|
||
return {}; | ||
} | ||
|
||
} // namespace Expr | ||
} // namespace Common | ||
} // namespace Filters | ||
} // namespace Extensions | ||
} // namespace Envoy |
Oops, something went wrong.