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 https://github.com/envoyproxy/envoy/issues/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 <kuat@google.com>

Mirrored from https://github.com/envoyproxy/envoy @ f90e1b08ac5b4973c45a6529780ebdd211ff901f
pull/620/head
data-plane-api(CircleCI) 6 years ago
parent 507755f731
commit 0392a9a417
  1. 7
      bazel/api_build_system.bzl
  2. 10
      envoy/config/rbac/v2/BUILD
  3. 8
      envoy/config/rbac/v2/rbac.proto

@ -23,13 +23,13 @@ def _LibrarySuffix(library_name, suffix):
# TODO(htuch): Convert this to native py_proto_library once # TODO(htuch): Convert this to native py_proto_library once
# https://github.com/bazelbuild/bazel/issues/3935 and/or # https://github.com/bazelbuild/bazel/issues/3935 and/or
# https://github.com/bazelbuild/bazel/issues/2626 are resolved. # https://github.com/bazelbuild/bazel/issues/2626 are resolved.
def api_py_proto_library(name, srcs = [], deps = [], has_services = 0): def api_py_proto_library(name, srcs = [], deps = [], external_py_proto_deps = [], has_services = 0):
_py_proto_library( _py_proto_library(
name = _Suffix(name, _PY_SUFFIX), name = _Suffix(name, _PY_SUFFIX),
srcs = srcs, srcs = srcs,
default_runtime = "@com_google_protobuf//:protobuf_python", default_runtime = "@com_google_protobuf//:protobuf_python",
protoc = "@com_google_protobuf//:protoc", protoc = "@com_google_protobuf//:protoc",
deps = [_LibrarySuffix(d, _PY_SUFFIX) for d in deps] + [ deps = [_LibrarySuffix(d, _PY_SUFFIX) for d in deps] + external_py_proto_deps + [
"@com_envoyproxy_protoc_gen_validate//validate:validate_py", "@com_envoyproxy_protoc_gen_validate//validate:validate_py",
"@com_google_googleapis//google/rpc:status_py_proto", "@com_google_googleapis//google/rpc:status_py_proto",
"@com_google_googleapis//google/api:annotations_py_proto", "@com_google_googleapis//google/api:annotations_py_proto",
@ -116,6 +116,7 @@ def api_proto_library(
deps = [], deps = [],
external_proto_deps = [], external_proto_deps = [],
external_cc_proto_deps = [], external_cc_proto_deps = [],
external_py_proto_deps = [],
has_services = 0, has_services = 0,
linkstatic = None, linkstatic = None,
require_py = 1): require_py = 1):
@ -152,7 +153,7 @@ def api_proto_library(
) )
py_export_suffixes = [] py_export_suffixes = []
if (require_py == 1): if (require_py == 1):
api_py_proto_library(name, srcs, deps, has_services) api_py_proto_library(name, srcs, deps, external_py_proto_deps, has_services)
py_export_suffixes = ["_py", "_py_genproto"] py_export_suffixes = ["_py", "_py_genproto"]
# Allow unlimited visibility for consumers # Allow unlimited visibility for consumers

@ -5,6 +5,15 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_go_proto_library", "api_prot
api_proto_library_internal( api_proto_library_internal(
name = "rbac", name = "rbac",
srcs = ["rbac.proto"], srcs = ["rbac.proto"],
external_cc_proto_deps = [
"@com_google_googleapis//google/api/expr/v1alpha1:syntax_cc_proto",
],
external_proto_deps = [
"@com_google_googleapis//google/api/expr/v1alpha1:syntax_proto",
],
external_py_proto_deps = [
"@com_google_googleapis//google/api/expr/v1alpha1:syntax_py_proto",
],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//envoy/api/v2/core:address", "//envoy/api/v2/core:address",
@ -22,5 +31,6 @@ api_go_proto_library(
"//envoy/api/v2/route:route_go_proto", "//envoy/api/v2/route:route_go_proto",
"//envoy/type/matcher:metadata_go_proto", "//envoy/type/matcher:metadata_go_proto",
"//envoy/type/matcher:string_go_proto", "//envoy/type/matcher:string_go_proto",
"@com_google_googleapis//google/api/expr/v1alpha1:cel_go_proto",
], ],
) )

@ -7,6 +7,8 @@ import "envoy/api/v2/route/route.proto";
import "envoy/type/matcher/metadata.proto"; import "envoy/type/matcher/metadata.proto";
import "envoy/type/matcher/string.proto"; import "envoy/type/matcher/string.proto";
import "google/api/expr/v1alpha1/syntax.proto";
package envoy.config.rbac.v2; package envoy.config.rbac.v2;
option java_outer_classname = "RbacProto"; option java_outer_classname = "RbacProto";
@ -81,7 +83,7 @@ message RBAC {
// Policy specifies a role and the principals that are assigned/denied the role. A policy matches if // Policy specifies a role and the principals that are assigned/denied the role. A policy matches if
// and only if at least one of its permissions match the action taking place AND at least one of its // and only if at least one of its permissions match the action taking place AND at least one of its
// principals match the downstream. // principals match the downstream AND the condition is true if specified.
message Policy { message Policy {
// Required. The set of permissions that define a role. Each permission is matched with OR // Required. The set of permissions that define a role. Each permission is matched with OR
// semantics. To match all actions for this policy, a single Permission with the `any` field set // semantics. To match all actions for this policy, a single Permission with the `any` field set
@ -92,6 +94,10 @@ message Policy {
// principal is matched with OR semantics. To match all downstreams for this policy, a single // principal is matched with OR semantics. To match all downstreams for this policy, a single
// Principal with the `any` field set to true should be used. // Principal with the `any` field set to true should be used.
repeated Principal principals = 2 [(validate.rules).repeated .min_items = 1]; repeated Principal principals = 2 [(validate.rules).repeated .min_items = 1];
// An optional symbolic expression specifying an access control condition.
// The condition is combined with AND semantics.
google.api.expr.v1alpha1.Expr condition = 3;
} }
// Permission defines an action (or actions) that a principal can take. // Permission defines an action (or actions) that a principal can take.

Loading…
Cancel
Save