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

Add negative RBAC label matches #4253

Merged
merged 4 commits into from
Sep 29, 2020
Merged

Add negative RBAC label matches #4253

merged 4 commits into from
Sep 29, 2020

Conversation

awly
Copy link
Contributor

@awly awly commented Aug 24, 2020

Normal node_labels matches are positive only - you must provide an
exact label or a regexp that matches all valid labels.

This is problematic when you want to grant access to "any label value
except X". You could put "X" in the deny section of a role, but this
prevents any other roles from granting access to "X". For example, users
may have many dynamically-named environments and one "prod", and they
want to only grant access to "prod" via workflow API.

This PR adds support for {{regexp.not_match(...)}} function (along with {{regexp.match(...)}}, for example {{regexp.not_match("prod")}}.

Fixes #3454

@awly awly force-pushed the andrew/rbac_negative_regexp branch 2 times, most recently from 8dc73ba to c52a4ea Compare August 26, 2020 21:28
@awly awly requested a review from benarent August 26, 2020 21:29
@@ -48,8 +48,19 @@ func ReplaceRegexp(expression string, replaceWith string, input string) (string,

// SliceMatchesRegex checks if input matches any of the expressions. The
// match is always evaluated as a regex either an exact match or regexp.
//
// If any expression starts with "+not ", the match is negated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update the comments to reflect the updated expression.

@klizhentas
Copy link
Contributor

@awly can you take a look at:

https://github.com/gravitational/teleport/blob/master/lib/utils/parse/parse.go

I would like us to use the "ast" approach for all function calls. As you can spot above, there is a function "email.local" that also supports variable syntax, so it will support {{regexp.not(}} as well.

Ping me if you want to discuss in more detail.

@awly
Copy link
Contributor Author

awly commented Aug 27, 2020

@klizhentas WDYT about going all-in on https://golang.org/pkg/text/template/ instead?

It looks like https://github.com/gravitational/teleport/blob/master/lib/utils/parse/parse.go just reimplements a subset of that, except for input data access ({{.external.foo}} being {{external.foo}}, which we can plumb as another func: https://play.golang.org/p/YwBaMRbj3g7)

@awly awly force-pushed the andrew/rbac_negative_regexp branch from c52a4ea to b2b5213 Compare September 8, 2020 19:01
@awly
Copy link
Contributor Author

awly commented Sep 8, 2020

Chatted with @klizhentas, we can use text/template as long as it's backwards-compatible.
A small prototype to show how to get that working with our claims syntax: https://play.golang.org/p/vOtNwdvr9Dq

I'll flesh that out and re-open this PR.

@awly awly marked this pull request as draft September 8, 2020 22:50
@awly
Copy link
Contributor Author

awly commented Sep 8, 2020

After more chatting, @klizhentas convinced me to go with our custom DSL.
text/template is flexible but its syntax is unfamiliar to non-Go developers.
A custom DSL gives us better UX at the cost of internal complexity.

@awly awly force-pushed the andrew/rbac_negative_regexp branch from b2b5213 to 9d71803 Compare September 17, 2020 20:19
@awly awly marked this pull request as ready for review September 17, 2020 20:20
@awly awly requested a review from benarent September 17, 2020 20:21
@awly
Copy link
Contributor Author

awly commented Sep 17, 2020

PTAL everyone, migrated this PR to use lib/utils/parse.
The new format supports {{regexp.match(...)}} and {{regexp.not_match(...)}}, along with the previous behavior.

@webvictim
Copy link
Contributor

LGTM, I'll leave it for others to take a look.

Copy link
Contributor

@benarent benarent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So based on the issue example, this would be

role: {{regexp.not_match(`^critical$`)}}

@awly
Copy link
Contributor Author

awly commented Sep 29, 2020

@benarent almost:

kind: role
version: v3
metadata:
  name: dev
spec:
  allow:
    node_labels:
      'environment': '{{regexp.not_match("^prod$")}}'

Andrew Lytvynov added 4 commits September 29, 2020 09:53
No need to handle literal expressions (e.g. without "{{foo.bar}}"
substitutions) at the higher level. Something like "foo" is a valid
expression which always returns "foo" regardless of traits.
Matchers use a similar syntax to Expressions, but behave differently:
- Expressions get evaluated - they interpolate some values and return a
  final string.
- Matchers check whether some string matches a value

Matchers implement the same logic as utils.SliceMatchesRegex and add 2
new functions:
- {{regexp.match("foo")}} - match input against a raw regex
- {{regexp.not_match("foo")}} - same as match, but inverts the result
This should be backwards-compatible plus add the {{regexp.match(...)}}
and {{regexp.not_match(...)}} functions.
@awly awly force-pushed the andrew/rbac_negative_regexp branch from 9d71803 to 45cf28b Compare September 29, 2020 16:56
@awly awly merged commit 75d7fbb into master Sep 29, 2020
@awly awly deleted the andrew/rbac_negative_regexp branch September 29, 2020 21:25
@benarent benarent added this to the 4.4 "Rome" milestone Sep 30, 2020
Copy link
Contributor

@klizhentas klizhentas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@awly Please take a look at my comments when you have a chance.

@@ -491,8 +491,8 @@ func (u *UserV1) Check() error {
return trace.BadParameter("user name cannot be empty")
}
for _, login := range u.AllowedLogins {
_, err := parse.RoleVariable(login)
if err == nil {
e, err := parse.NewExpression(login)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New logic only returns error in case of grammatic errors and accepts literal values. Doesn't it mean that now err should return error here if it's not nil? Otherwise the following will be accepted: {{regexp_not_match(...) which probably should not be ignored as it's a typo and we should let users know


re, err := regexp.Compile(raw)
if err != nil {
return nil, trace.BadParameter("failed parsing regexp %q: %v", raw, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May make sense to send a link to our documentation with examples. This will make our message actionable and reduce amount of support.

return nil, trace.Wrap(err)
}
// If this is not_match, wrap the regexpMatcher to invert
// it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra newline?

@awly
Copy link
Contributor Author

awly commented Oct 1, 2020

@klizhentas thanks for the comments, addressed in #4430

awly pushed a commit that referenced this pull request Oct 2, 2020
Also improve error output and user validation.
Based on missed feedback in #4253 (review)
awly pushed a commit that referenced this pull request Oct 5, 2020
Also improve error output and user validation.
Based on missed feedback in #4253 (review)
awly pushed a commit that referenced this pull request Oct 15, 2020
This change was not backwards compatible - variable interpolation should
work in node_labels.

This commit partially reverts
#4253 and
#4430
awly pushed a commit that referenced this pull request Oct 15, 2020
This change was not backwards compatible - variable interpolation should
work in node_labels.

This commit partially reverts
#4253 and
#4430
awly pushed a commit that referenced this pull request Oct 15, 2020
This change was not backwards compatible - variable interpolation should
work in node_labels.

This commit partially reverts
#4253 and
#4430
russjones pushed a commit that referenced this pull request Oct 16, 2020
This change was not backwards compatible - variable interpolation should
work in node_labels.

This commit partially reverts
#4253 and
#4430
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support regexp negation for role labels
5 participants