-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
rbac: add rbac network filter. #4083
Conversation
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a quick pass, will do another pass later today or tomorrow
// If absent, no enforcing RBAC policy will be applied. | ||
config.rbac.v2alpha.RBAC rules = 1; | ||
|
||
// Shadow rules are not enforced by the filter (i.e., returning a 403) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not 403 for network filter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
const envoy::config::filter::http::rbac::v2::RBAC& proto_config, | ||
const std::string& stats_prefix, Stats::Scope& scope) | ||
: stats_(generateStats(stats_prefix, scope)), | ||
INIT_RBAC_ENGINES(proto_config, false /* disable_http_rules */) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need the INIT_RBAC_ENGINES
macro? You can call the other constructors here. Or extract common part to other class, commented in header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main reason is the type of proto_config
are different.
For RoleBasedAccessControlRouteSpecificFilterConfig
, the type is RBACPerRoute
;
For RoleBasedAccessControlFilterConfig
, the type is http::rbac::v2::RBAC
and network::rbac::v2::RBAC
respectively.
Do you prefer a template function instead of the INIT_RBAC_ENGINES
macro?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I would prefer a template function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
enum class EnforcementMode { Enforced, Shadow }; | ||
|
||
class RoleBasedAccessControlRouteSpecificFilterConfig : public Router::RouteSpecificFilterConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these two classes are pretty similar, you might want to combine them, or extract common part out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's copied directly from the existing implementation. I remember there had been some discussions in previous PR to combine them but later decided to not to do so. Also looked at the content, it seems there is not too much thing to share, do you have any specific suggestions here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, ok then I would not combine those config classes here, rather keep them separately.
callbacks_->connection().ssl()->subjectPeerCertificate() | ||
: "none"); | ||
|
||
const Envoy::Http::HeaderMapImpl empty_header; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onData is called multiple times. we should only do rbac check on the first one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can cache the check result, but just curious could it have any problems with the cache? I assume the cert, ip and port won't change on following onData()? Also whenever a newer RBAC config is pushed, envoy will create a new network filter, so it's also no problem to cache the result?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Envoy will call onData() for each buffer it receives. But you don't need to call rbac check on every buffer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
const envoy::config::filter::network::rbac::v2::RBAC& proto_config, | ||
Server::Configuration::FactoryContext& context) { | ||
Filters::Common::RBAC::RoleBasedAccessControlFilterConfigSharedPtr config( | ||
new Filters::Common::RBAC::RoleBasedAccessControlFilterConfig(proto_config, context.scope())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use make_shared
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
LGTM |
const envoy::config::filter::http::rbac::v2::RBAC& proto_config, | ||
const std::string& stats_prefix, Stats::Scope& scope) | ||
: stats_(generateStats(stats_prefix, scope)), | ||
INIT_RBAC_ENGINES(proto_config, false /* disable_http_rules */) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I would prefer a template function
const envoy::api::v2::core::Metadata& metadata, | ||
std::string* effective_policy_id) const PURE; | ||
virtual bool | ||
allowed(const Network::Connection& connection, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://google.github.io/styleguide/cppguide.html#Default_Arguments
Default arguments are banned on virtual functions, where they don't work properly, and in cases where the specified default might not evaluate to the same value depending on when it was evaluated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks for the reference!
|
||
enum class EnforcementMode { Enforced, Shadow }; | ||
|
||
class RoleBasedAccessControlRouteSpecificFilterConfig : public Router::RouteSpecificFilterConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, ok then I would not combine those config classes here, rather keep them separately.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
RoleBasedAccessControlFilter::checkEngine(Filters::Common::RBAC::EnforcementMode mode) { | ||
const auto& engine = config_->engine(nullptr, NetworkFilterNames::get().Rbac, mode); | ||
if (engine.has_value()) { | ||
if (engine->allowed(callbacks_->connection(), Envoy::Http::HeaderMapImpl(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a fan of leaking Http::HeaderMapImpl into network filters, can you add another allowed
in to engine interface and call it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
case envoy::config::rbac::v2alpha::Permission::RuleCase::kHeader: | ||
if (disable_http_rules) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this again, why do we need disable_http_rules
and ignore HTTP headers? When a HeaderMatch specified in TCP filter, shouldn't it just reject the configuration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense for me to treat this simply as a deny, @liminw WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. If a rules says "allow requests with header a=foo", the rules does not match in TCP filter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to reject invalid rules in config.cc
: absl::nullopt; | ||
} | ||
|
||
class RoleBasedAccessControlRouteSpecificFilterConfig : public Router::RouteSpecificFilterConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you should move this to Common
, it is HTTP specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved back to the http filer.
/** | ||
* Configuration for the RBAC filter. | ||
*/ | ||
class RoleBasedAccessControlFilterConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not convinced we should combine HTTP RBAC and TCP RBAC config in same class. You can have template functions and stats macros above in common/rbac/config_utility. having a filter_name in engine()
below seems hacky.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, Moved back to http and network filter respectively, renamed config
to utility
.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
@@ -0,0 +1,31 @@ | |||
syntax = "proto3"; | |||
|
|||
package envoy.config.filter.network.rbac.v2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is version defined? Is "v2" the correct version? @lizan @mattklein123
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's defined the same to rbac http proto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes v2 is correct, all envoy protos are under v2 (or v2alpha, v2beta)
@@ -159,7 +160,8 @@ message Principal { | |||
// A header (or psuedo-header such as :path or :method) on the incoming HTTP request. | |||
envoy.api.v2.route.HeaderMatcher header = 6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"only available for HTTP requests" comments apply here as well.
The RBAC network filter is used to authorize actions (permissions) by identified downstream clients | ||
(principals). This is useful to explicitly manage callers to an application and protect it from | ||
unexpected or forbidden agents. The filter supports configuration with either a safe-list (ALLOW) or | ||
block-list (DENY) set of policies based off properties of the connection (IPs, ports, SSL subject). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
based off => based on?
(principals). This is useful to explicitly manage callers to an application and protect it from | ||
unexpected or forbidden agents. The filter supports configuration with either a safe-list (ALLOW) or | ||
block-list (DENY) set of policies based off properties of the connection (IPs, ports, SSL subject). | ||
This filter also supports policy in both enforcement and shadow mode, shadow mode won't effect real |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mode => modes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shadow mode => Shadow mode (New sentence starts)
case envoy::config::rbac::v2alpha::Permission::RuleCase::kHeader: | ||
if (disable_http_rules) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. If a rules says "allow requests with header a=foo", the rules does not match in TCP filter.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
@@ -0,0 +1,31 @@ | |||
syntax = "proto3"; | |||
|
|||
package envoy.config.filter.network.rbac.v2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes v2 is correct, all envoy protos are under v2 (or v2alpha, v2beta)
address: | ||
socket_address: | ||
address: 127.0.0.1 | ||
port_value: 8000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do not use fixed port number for tests (they couldn't be ran parallel, and fail for port conflict, which is hard to notice)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, we don't really need port in rbac config.
@@ -37,6 +39,11 @@ bool RoleBasedAccessControlEngineImpl::allowed(const Network::Connection& connec | |||
return matched == allowed_if_matched_; | |||
} | |||
|
|||
bool RoleBasedAccessControlEngineImpl::allowed(const Network::Connection& connection) const { | |||
return allowed(connection, Envoy::Http::HeaderMapImpl(), envoy::api::v2::core::Metadata(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make the HeaderMapImpl and Metadata static variable? HeaderMapImpl does memory allocations for inline headers, constructing everytime is not good. Ideally we should make the parameter a pointer instead of a reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
namespace NetworkFilters { | ||
namespace RBACFilter { | ||
|
||
enum EngineResult { UNKNOWN, NONE, ALLOW, DENY }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When is NONE result used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NONE means no rbac policies, later we then don't increase metrics (allowed or denied).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Thanks for explaining.
namespace NetworkFilters { | ||
namespace RBACFilter { | ||
|
||
class RoleBasedAccessControlNetworkFilterTest : public testing::Test { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any tests on identity (SAN field in cert)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no such test in both http and tcp rbac filter so far.
namespace RBACFilter { | ||
|
||
static void validateFail(const std::string& header, const std::string& metadata) { | ||
throw EnvoyException(fmt::format("Found header({}) or metadata({}) rule," |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will users see if validation fail? The request being rejected? With what error code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message should be returned to the entity trying to apply the config. In Istio, this should be Pilot. There is no request at this point, this happens in config stage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Good point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I'm not sure if it's really worth doing this, but up to you. It's going to be difficult to keep this in sync when new features are added to the HTTP filter and IMO it's fine to just ignore fields that are not supported at the network layer. But don't feel that strongly about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Matt, I'll keep the validation logic for now and also keep an eye on this if it becomes a big problem between the HTTP filter and the network filter.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
namespace NetworkFilters { | ||
namespace RBACFilter { | ||
|
||
enum EngineResult { UNKNOWN, NONE, ALLOW, DENY }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enum values using PascalCase (e.g., RoundRobin).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great. Few small comments. Please check that you have 100% code coverage on the new filter.
// RBAC network filter config. | ||
// | ||
// Header and Metadata should not be used in rules/shadow_rules in RBAC network filter as | ||
// these information are only available in :ref:`RBAC http filter <config_http_filters_rbac>`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/these information are/this information is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -69,6 +69,7 @@ EXTENSIONS = { | |||
"envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config", | |||
"envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config", | |||
"envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config", | |||
"envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: please alpha order (can you check to see if anything else is not ordered)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also reordered HTTP filters.
namespace RBACFilter { | ||
|
||
static void validateFail(const std::string& header, const std::string& metadata) { | ||
throw EnvoyException(fmt::format("Found header({}) or metadata({}) rule," |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I'm not sure if it's really worth doing this, but up to you. It's going to be difficult to keep this in sync when new features are added to the HTTP filter and IMO it's fine to just ignore fields that are not supported at the network layer. But don't feel that strongly about it.
: "none"); | ||
|
||
if (shadow_engine_result_ == Unknown) { | ||
// TODO(quanlin): Support metric collection for RBAC network filter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you check metrics below? Is this comment still valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry the comment might be a bit misleading, I actually mean to pass the metrics to other filers. In HTTP filter this is done by using metadata. But there is no metadata for TCP filter so I created this TODO.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
@mattklein123 |
docs/root/intro/version_history.rst
Outdated
@@ -49,6 +49,7 @@ Version history | |||
boolean flag in the ratelimit configuration. | |||
Support for the legacy proto :repo:`source/common/ratelimit/ratelimit.proto` is deprecated and will be removed at the start of the 1.9.0 release cycle. | |||
* rest-api: added ability to set the :ref:`request timeout <envoy_api_field_core.ApiConfigSource.request_timeout>` for REST API requests. | |||
* rbac network filter: a :ref:`role-based access control network filter <config_network_filters_rbac>` has been added. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this should be one line above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, very nice!
* origin/master: (38 commits) test: add tests for corner-cases around sending requests before run() starts or after run() ends. (envoyproxy#4114) perf: reduce the memory usage of LC Trie construction (envoyproxy#4117) test: moving redundant code in websocket_integration_test to utilities (envoyproxy#4127) test: make YamlLoadFromStringFail less picky about error msg. (envoyproxy#4141) rbac: add rbac network filter. (envoyproxy#4083) fuzz: route lookup and header finalization fuzzer. (envoyproxy#4116) Set content-type and content-length (envoyproxy#4113) fault: use FractionalPercent for percent (envoyproxy#3978) test: Fix inverted exact match logic in IntegrationTcpClient::waitForData() (envoyproxy#4134) Added cluster_name to load assignment config for static cluster (envoyproxy#4123) ssl: refactor ContextConfig to use TlsCertificateConfig (envoyproxy#4115) syscall: refactor OsSysCalls for deeper errno latching (envoyproxy#4111) thrift_proxy: fix oneway bugs (envoyproxy#4025) Do not crash when converting YAML to JSON fails (envoyproxy#4110) config: allow unknown fields flag (take 2) (envoyproxy#4096) Use a jittered backoff strategy for handling HdsDelegate stream/connection failures (envoyproxy#4108) bazel: use GCS remote cache (envoyproxy#4050) Add thread local cache of overload action states (envoyproxy#4090) Added TCP healthcheck capabilities to the HdsDelegate (envoyproxy#4079) secret: add secret provider interface and use it for TlsCertificates (envoyproxy#4086) ... Signed-off-by: Snow Pettersen <snowp@squareup.com>
Description:
Risk Level: Medium.
Testing: Added unit test and integration test for the new network filter. Also tested manually with Envoy built with the RBAC network filter.
Docs Changes: Added network filter docs.
Release Notes: Added for the RBAC network filter.
Fixes #3993
/cc @quanjielin I add a TODO for collecting metrics for shadow rules in the RBAC network filter.
/cc @liminw @lizan @mattklein123
Signed-off-by: Yangmin Zhu ymzhu@google.com