rbac: add metadata support to rbac filter#3638
rbac: add metadata support to rbac filter#3638mattklein123 merged 7 commits intoenvoyproxy:masterfrom
Conversation
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
api/envoy/api/v2/core/base.proto
Outdated
|
|
||
| // If specified, value match will be performed based on double value. It's matched if and only | ||
| // if the target value is a double value and is equal to this field. | ||
| double number_match = 2; |
There was a problem hiding this comment.
floating number is (almost) impossible to do exact match, consider add a range match like envoy.type.Int64Range
There was a problem hiding this comment.
Updated to use a DoubleMatcher.
api/envoy/api/v2/core/base.proto
Outdated
| // Examples: | ||
| // | ||
| // * *abc* will only match to string value *abc*. | ||
| string exact_match = 3; |
api/envoy/api/v2/core/base.proto
Outdated
| // Specifies how to match a value. Only have effect on primitive value. | ||
| // For non-primitive value, it's always not matched. | ||
| oneof match_specifier { | ||
| option (validate.required) = true; |
There was a problem hiding this comment.
instead of listing all combination of type and its matcher here, would it be clearer to make a Matcher type (with oneof) for each type and have a oneof here?
i.e.:
message NullMatch {
}
message NumberMatch {
oneof match {
Int64Range range_match = 1;
int64 exact_match = 2;
}
}
message ValueMatch {
oneof match {
NullMatch null = 1;
NumberMatch number = 2;
StringMatch string = 3;
...
}
}
There was a problem hiding this comment.
I think that makes sense for string, number or other non primitive type, for bool I just kept it there as it's much simpler to use and also easy to compare in the code.
api/envoy/api/v2/core/base.proto
Outdated
| // | ||
| // An example use of MetadataMatcher is specifying additional metadata in envoy.filters.http.rbac to | ||
| // enforce access control based on dynamic metadata in a request. | ||
| message MetadataMatcher { |
There was a problem hiding this comment.
I have a general comment here about something that's been bothering me that I think we should get on top of now before it gets even worse.
We have a ton of different matching constructs in the API that at the end of the day are all basically the same. This includes and/or logic, headers, matching on various other things such as response flags, health check, etc.
I suggest we make a new namespace/folder called "matchers" and start moving all of the different matchers into that namespace. Also, I just looked, and is StringMatch used anywhere? I don't think so, so can we delete it as part of this change?
Thoughts? cc @htuch
There was a problem hiding this comment.
I can start this change in this PR, (Initially only for MetadataMatcher and then move other Matchers to it in follow up PR).
As for StringMatch, I think it makes sense to make it a standalone Matcher under that new directory/namespace, at least it could be used by this MetadataMatcher.
Should I start it right in this PR? Thanks!
cc @lizan
There was a problem hiding this comment.
+1 I would like to see more standardization across the entire API of these match patterns.
There was a problem hiding this comment.
Added a new directory api/envoy/type/matchers and namespace Envoy::Matchers for the matchers. Currently it doesn't include and/or logic, headers, health check, etc. I prefer to do those in a different PR with a clear description of use case and goal.
Thanks!
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
c748067 to
5d009dc
Compare
| @@ -0,0 +1,59 @@ | |||
| syntax = "proto3"; | |||
|
|
|||
| package envoy.type.matchers; | |||
| string filter = 1 [(validate.rules).string.min_bytes = 1]; | ||
|
|
||
| // Required. The multi-key path to retrieve the Value from the Struct. | ||
| repeated string path = 2 [(validate.rules).repeated .min_items = 1]; |
There was a problem hiding this comment.
it is unclear to me how this works for array, notes?
There was a problem hiding this comment.
Added comment, it's actually not supported and will just result a not match.
| // If specified, value match will be performed based on whether the path is referring to a | ||
| // valid primitive value in the metadata. If the path is referring to a non-primitive value, | ||
| // the result is always not matched. | ||
| bool present_match = 7; |
| oneof match_pattern { | ||
| option (validate.required) = true; | ||
|
|
||
| // If specified, it's matched if and only if the target value is a NullValue and this field is |
There was a problem hiding this comment.
what is the use case of setting null_match to false? (same for below)
There was a problem hiding this comment.
Not too much real use case, I make it a bool to simplify the Value design and also feel it's easier to set a bool field for the user.
There was a problem hiding this comment.
I would prefer using a empty message, like here:
https://github.com/envoyproxy/envoy/blob/master/api/envoy/api/v2/route/route.proto#L763
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
| @@ -0,0 +1,60 @@ | |||
| syntax = "proto3"; | |||
|
|
|||
| package envoy.type.matcher; | |||
There was a problem hiding this comment.
You'll need to rename directories etc..
| oneof match_pattern { | ||
| option (validate.required) = true; | ||
|
|
||
| // If specified, it's matched if and only if the target value is a NullValue and this field is |
There was a problem hiding this comment.
I would prefer using a empty message, like here:
https://github.com/envoyproxy/envoy/blob/master/api/envoy/api/v2/route/route.proto#L763
| string filter = 1 [(validate.rules).string.min_bytes = 1]; | ||
|
|
||
| // Required. The multi-key path to retrieve the Value from the Struct. | ||
| // Note: Array is not supported and will result a not match discarding any specified values. |
There was a problem hiding this comment.
Still questionable how this will be extended to support array. Will it make more sense that each segment be oneof (key, index)? Needs more thoughts.
There was a problem hiding this comment.
Updated to a message with a oneof field. We only need string key for now and the current metadataValue() also only supports string keys.
It should be easy to extend to include index, I prefer to do that when we really need to use a index or at least in a different PR.
There was a problem hiding this comment.
yeah that's no problem, metadataValue take Metadata as input, so it could contain array anyway. make this PathSegment will allow us to extend this to support array without breaking backward compatibility.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
a40489a to
5d4e01d
Compare
|
@mattklein123 @htuch PTAL, Thank you! |
|
Sorry for the delay I will get to this tomorrow. |
mattklein123
left a comment
There was a problem hiding this comment.
Thanks for working on this! Some comments to get started.
| // A port number that describes the destination port connecting to. | ||
| uint32 destination_port = 6 [(validate.rules).uint32.lte = 65535]; | ||
|
|
||
| // A metadata that describes additional information about the action. |
There was a problem hiding this comment.
nit: s/A metadata/Metadata. Same below.
| // :ref:`Metadata <envoy_api_msg_core.Metadata>`. It uses `filter` and `path` to retrieve the value | ||
| // from the Metadata and then check if it's matched to one of the specified values. | ||
| // | ||
| // An example use of MetadataMatcher is specifying additional metadata in envoy.filters.http.rbac to |
There was a problem hiding this comment.
nit: ref link to rbac message.
| // enforce access control based on dynamic metadata in a request. | ||
| message MetadataMatcher { | ||
| // Specifies the segment in a path to retrieve value from Metadata. | ||
| // Note: Array is not supported for now and will result a not match directly. |
There was a problem hiding this comment.
What does this mean exactly? Clarify?
| } | ||
| } | ||
|
|
||
| // Specifies the value to match. Only primitive value is supported. For non-primitive value, the |
There was a problem hiding this comment.
"are supported", "For non-primitive values,"
| oneof match_pattern { | ||
| option (validate.required) = true; | ||
|
|
||
| // If specified, it's matched if and only if the target value is a NullValue. |
There was a problem hiding this comment.
s/it's matched/a match occurs (same in all cases below)
| // Required. The filter name to retrieve the Struct from the Metadata. | ||
| string filter = 1 [(validate.rules).string.min_bytes = 1]; | ||
|
|
||
| // Required. The path to retrieve the Value from the Struct. |
There was a problem hiding this comment.
I don't really understand what a path segment is in this context. Can you add a full formed config example at the top of this file and explain it?
| bool DoubleMatcher::match(double value) const { | ||
| switch (matcher_.match_pattern_case()) { | ||
| case envoy::type::matcher::DoubleMatcher::kRange: | ||
| return matcher_.range().start() <= value && value < matcher_.range().end(); |
There was a problem hiding this comment.
The docs don't specify whether the ranges are inclusive or exclusive, and it seems a bit odd to to inclusive at start but not at end? Any reason for this? Either way please clearly document.
There was a problem hiding this comment.
I did this to follow the current Int64Range, The document mentioned this in the definition of DoubleRange, also added it to DoubleMatcher.
source/common/common/matchers.cc
Outdated
| case envoy::type::matcher::DoubleMatcher::kExact: | ||
| return matcher_.exact() == value; | ||
| default: | ||
| return false; |
There was a problem hiding this comment.
This is checked by schema, switch to NOT_REACHED. Same elsewhere.
|
|
||
| private: | ||
| const envoy::type::matcher::StringMatcher matcher_; | ||
| std::regex regex_; |
There was a problem hiding this comment.
Why isn't this part of StringMatcher?
There was a problem hiding this comment.
StringMatcher proto has this but only as a simple string field, here I copy the parsed regex to a local variable so that it could be used directly later.
|
|
||
| // Required. A set of values to match. The MetadataMatcher is matched if at least one value is | ||
| // matched, in other words, it's matched with OR semantics. | ||
| repeated Value values = 3 [(validate.rules).repeated .min_items = 1]; |
There was a problem hiding this comment.
From looking at the code, IMO it's kind of strange that you force a matcher to be all one top of match. Why can't people just use multiple metadata matchers and make each match singular? Then all the normal and/or/etc. logic applies? I'm not sure we really need repeated in here?
There was a problem hiding this comment.
A repeated values make it easier to use when a user want to share the same common information in a metadata matcher, like the filter and path field and I think this is what we need in most cases.
For more advanced and/or/etc. logic, I feel it's better to make it out of the MetadataMatcher, just like the existing RBAC matcher behavior: each specific matcher do one simple thing to match a single value, an outer and/or wrapper matcher is used to support complex and/or logic.
There was a problem hiding this comment.
In Envoy we don't optimize for human use but instead assume machine generation. I don't think there is any need to have multiple ways of doing things, so I think we can remove the repeated here unless there is something that cannot be done otherwise? Thank you!
There was a problem hiding this comment.
I see, thanks for the explanation, removed repeated here.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
dad21f5 to
530dedd
Compare
| string filter = 1 [(validate.rules).string.min_bytes = 1]; | ||
|
|
||
| // The path to retrieve the Value from the Struct. | ||
| repeated PathSegment path = 2 [(validate.rules).repeated .min_items = 1]; |
There was a problem hiding this comment.
Shall we use fieldmask? You can write paths as "a.b.c".
There was a problem hiding this comment.
Fieldmask have some disadvantages, from my investigations, it cannot support either list or map which means we cannot use it directly here, unless we implement it by ourselves in Envoy.
Second, the semantics would be much more complicated, it won't be a.b.c, you have to list all field names in the path, so it will be something like fields.a.struct_value.fields.b.struct_value.fields.c.string_value.
Last, Envoy already have a well implemented metadataValue() method which satisfy all our needs here, so I think no need to use the fieldmask anymore.
Signed-off-by: Yangmin Zhu <ymzhu@google.com>
|
@mattklein123 |
Signed-off-by: Yangmin Zhu ymzhu@google.com
title: rbac: add metadata support to rbac filter
Description:
api/envoy/type/matcher/{metadata.proto, number.proto, string.proto}and namespaceEnvoy::Matcherfor matcherMetadataMatchermessage to support a generic way to match a value in the MetadataMetadataMatcherand pass the metadata from thedynamicMetadata()Risk Level: Low
Testing:
Added and ran unit tests.
Docs Changes:
N/A
Release Notes:
N/A
Fixes #3624
cc @lizan @liminw @diemtvu @qiwzhang @quanjielin