Skip to content

Commit

Permalink
MAISTRA-224: Support for authorisation regex matching on request headers
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Grimm <dgrimm@redhat.com>
  • Loading branch information
2 people authored and mergify[bot] committed Jan 22, 2020
1 parent 1343d77 commit b0f47f2
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 2 deletions.
14 changes: 14 additions & 0 deletions pilot/pkg/security/authz/model/matcher/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"strings"

route "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher"
)

// HeaderMatcher converts a key, value string pair to a corresponding HeaderMatcher.
Expand Down Expand Up @@ -53,3 +54,16 @@ func HeaderMatcher(k, v string) *route.HeaderMatcher {
},
}
}

// HeaderMatcherRegex converts a key, value string pair to a corresponding SafeRegex HeaderMatcher.
func HeaderMatcherRegex(k, v string) *route.HeaderMatcher {
return &route.HeaderMatcher{
Name: k,
HeaderMatchSpecifier: &route.HeaderMatcher_SafeRegexMatch{
SafeRegexMatch: &matcher.RegexMatcher{
EngineType: &matcher.RegexMatcher_GoogleRe2{GoogleRe2: &matcher.RegexMatcher_GoogleRE2{}},
Regex: v,
},
},
}
}
3 changes: 2 additions & 1 deletion pilot/pkg/security/authz/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const (
RBACTCPFilterStatPrefix = "tcp."

// attributes that could be used in both ServiceRoleBinding and ServiceRole.
attrRequestHeader = "request.headers" // header name is surrounded by brackets, e.g. "request.headers[User-Agent]".
attrRequestHeader = "request.headers" // header name is surrounded by brackets, e.g. "request.headers[User-Agent]".
attrRequestRegexHeader = "request.regex.headers" // header name is surrounded by brackets, e.g. "request.headers.regex[User-Agent]".

// attributes that could be used in a ServiceRoleBinding property.
attrSrcIP = "source.ip" // supports both single ip and cidr, e.g. "10.1.2.3" or "10.1.0.0/16".
Expand Down
13 changes: 12 additions & 1 deletion pilot/pkg/security/authz/model/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (permission *Permission) ValidateForTCP(forTCP bool) error {
}
for _, constraint := range permission.Constraints {
for k := range constraint {
if strings.HasPrefix(k, attrRequestHeader) {
if strings.HasPrefix(k, attrRequestHeader) || strings.HasPrefix(k, attrRequestRegexHeader) {
return fmt.Errorf("constraint(%v)", constraint)
}
}
Expand Down Expand Up @@ -207,6 +207,7 @@ func isSupportedPermission(key string) bool {
case key == attrDestPort:
case key == pathHeader || key == methodHeader || key == hostHeader:
case strings.HasPrefix(key, attrRequestHeader):
case strings.HasPrefix(key, attrRequestRegexHeader):
case key == attrConnSNI:
case strings.HasPrefix(key, "experimental.envoy.filters.") && isKeyBinary(key):
default:
Expand Down Expand Up @@ -252,6 +253,16 @@ func (permission *Permission) forKeyValues(key string, values []string) *envoy_r
m := matcher.HeaderMatcher(header, v)
return permissionHeader(m), nil
}
case strings.HasPrefix(key, attrRequestRegexHeader):
header, err := extractNameInBrackets(strings.TrimPrefix(key, attrRequestRegexHeader))
if err != nil {
rbacLog.Errorf("ignored invalid %s: %v", attrRequestRegexHeader, err)
return nil
}
converter = func(v string) (*envoy_rbac.Permission, error) {
m := matcher.HeaderMatcherRegex(header, v)
return permissionHeader(m), nil
}
case key == attrConnSNI:
converter = func(v string) (*envoy_rbac.Permission, error) {
m := matcher.StringMatcher(v, permission.v1beta1)
Expand Down
28 changes: 28 additions & 0 deletions pilot/pkg/security/authz/model/permission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,34 @@ func TestPermission_Generate(t *testing.T) {
exactMatch: tag-2
name: X-tag`,
},
{
name: "permission with constraint attrRequestRegexHeader",
permission: &Permission{
Constraints: []KeyValues{
{
fmt.Sprintf("%s[%s]", attrRequestRegexHeader, "X-id"): []string{"id-[0-9]"},
fmt.Sprintf("%s[%s]", attrRequestRegexHeader, "X-tag"): []string{"tag-[0-9]"},
},
},
},
wantYAML: `
andRules:
rules:
- orRules:
rules:
- header:
name: X-id
safeRegexMatch:
googleRe2: {}
regex: id-[0-9]
- orRules:
rules:
- header:
name: X-tag
safeRegexMatch:
googleRe2: {}
regex: tag-[0-9]`,
},
{
name: "permission with constraint attrConnSNI",
permission: &Permission{
Expand Down
9 changes: 9 additions & 0 deletions pilot/pkg/security/authz/model/principal.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ func isSupportedPrincipal(key string) bool {
case attrSrcPrincipal == key:
case found(key, []string{attrRequestPrincipal, attrRequestAudiences, attrRequestPresenter, attrSrcUser}):
case strings.HasPrefix(key, attrRequestHeader):
case strings.HasPrefix(key, attrRequestRegexHeader):
case strings.HasPrefix(key, attrRequestClaims):
default:
return false
Expand Down Expand Up @@ -255,6 +256,14 @@ func (principal *Principal) forKeyValue(key, value string, forTCPFilter bool) *e
}
m := matcher.HeaderMatcher(header, value)
return principalHeader(m)
case strings.HasPrefix(key, attrRequestRegexHeader):
header, err := extractNameInBrackets(strings.TrimPrefix(key, attrRequestRegexHeader))
if err != nil {
rbacLog.Errorf("ignored invalid %s: %v", attrRequestRegexHeader, err)
return nil
}
m := matcher.HeaderMatcherRegex(header, value)
return principalHeader(m)
case strings.HasPrefix(key, attrRequestClaims):
claim, err := extractNameInBrackets(strings.TrimPrefix(key, attrRequestClaims))
if err != nil {
Expand Down
24 changes: 24 additions & 0 deletions pilot/pkg/security/authz/model/principal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,30 @@ func TestPrincipal_Generate(t *testing.T) {
exactMatch: tag-2
name: X-tag`,
},
{
name: "principal with property attrRequestHeader",
principal: &Principal{
Properties: []KeyValues{
{
fmt.Sprintf("%s[%s]", attrRequestRegexHeader, "X-id"): []string{"id-[0-9]"},
fmt.Sprintf("%s[%s]", attrRequestRegexHeader, "X-tag"): []string{"tag-[0-9]"},
},
},
},
wantYAML: `
andIds:
ids:
- header:
name: X-id
safeRegexMatch:
googleRe2: {}
regex: id-[0-9]
- header:
name: X-tag
safeRegexMatch:
googleRe2: {}
regex: tag-[0-9]`,
},
{
name: "principal with property attrRequestClaims",
principal: &Principal{
Expand Down

0 comments on commit b0f47f2

Please sign in to comment.