Skip to content

Commit

Permalink
Add extra layer on top of RBAC Engine (#4576)
Browse files Browse the repository at this point in the history
* Add extra layer in RBAC
  • Loading branch information
zasweq authored Jul 22, 2021
1 parent a0bed72 commit c513103
Show file tree
Hide file tree
Showing 3 changed files with 885 additions and 402 deletions.
70 changes: 50 additions & 20 deletions internal/xds/rbac/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
// matcher is an interface that takes data about incoming RPC's and returns
// whether it matches with whatever matcher implements this interface.
type matcher interface {
match(data *RPCData) bool
match(data *rpcData) bool
}

// policyMatcher helps determine whether an incoming RPC call matches a policy.
Expand Down Expand Up @@ -63,7 +63,7 @@ func newPolicyMatcher(policy *v3rbacpb.Policy) (*policyMatcher, error) {
}, nil
}

func (pm *policyMatcher) match(data *RPCData) bool {
func (pm *policyMatcher) match(data *rpcData) bool {
// A policy matches if and only if at least one of its permissions match the
// action taking place AND at least one if its principals match the
// downstream peer.
Expand Down Expand Up @@ -202,7 +202,7 @@ type orMatcher struct {
matchers []matcher
}

func (om *orMatcher) match(data *RPCData) bool {
func (om *orMatcher) match(data *rpcData) bool {
// Range through child matchers and pass in data about incoming RPC, and
// only one child matcher has to match to be logically successful.
for _, m := range om.matchers {
Expand All @@ -219,7 +219,7 @@ type andMatcher struct {
matchers []matcher
}

func (am *andMatcher) match(data *RPCData) bool {
func (am *andMatcher) match(data *rpcData) bool {
for _, m := range am.matchers {
if !m.match(data) {
return false
Expand All @@ -234,7 +234,7 @@ func (am *andMatcher) match(data *RPCData) bool {
type alwaysMatcher struct {
}

func (am *alwaysMatcher) match(data *RPCData) bool {
func (am *alwaysMatcher) match(data *rpcData) bool {
return true
}

Expand All @@ -244,7 +244,7 @@ type notMatcher struct {
matcherToNot matcher
}

func (nm *notMatcher) match(data *RPCData) bool {
func (nm *notMatcher) match(data *rpcData) bool {
return !nm.matcherToNot.match(data)
}

Expand Down Expand Up @@ -284,8 +284,8 @@ func newHeaderMatcher(headerMatcherConfig *v3route_componentspb.HeaderMatcher) (
return &headerMatcher{matcher: m}, nil
}

func (hm *headerMatcher) match(data *RPCData) bool {
return hm.matcher.Match(data.MD)
func (hm *headerMatcher) match(data *rpcData) bool {
return hm.matcher.Match(data.md)
}

// urlPathMatcher matches on the URL Path of the incoming RPC. In gRPC, this
Expand All @@ -303,8 +303,8 @@ func newURLPathMatcher(pathMatcher *v3matcherpb.PathMatcher) (*urlPathMatcher, e
return &urlPathMatcher{stringMatcher: stringMatcher}, nil
}

func (upm *urlPathMatcher) match(data *RPCData) bool {
return upm.stringMatcher.Match(data.FullMethod)
func (upm *urlPathMatcher) match(data *rpcData) bool {
return upm.stringMatcher.Match(data.fullMethod)
}

// sourceIPMatcher and destinationIPMatcher both are matchers that match against
Expand All @@ -329,8 +329,8 @@ func newSourceIPMatcher(cidrRange *v3corepb.CidrRange) (*sourceIPMatcher, error)
return &sourceIPMatcher{ipNet: ipNet}, nil
}

func (sim *sourceIPMatcher) match(data *RPCData) bool {
return sim.ipNet.Contains(net.IP(net.ParseIP(data.PeerInfo.Addr.String())))
func (sim *sourceIPMatcher) match(data *rpcData) bool {
return sim.ipNet.Contains(net.IP(net.ParseIP(data.peerInfo.Addr.String())))
}

type destinationIPMatcher struct {
Expand All @@ -346,8 +346,8 @@ func newDestinationIPMatcher(cidrRange *v3corepb.CidrRange) (*destinationIPMatch
return &destinationIPMatcher{ipNet: ipNet}, nil
}

func (dim *destinationIPMatcher) match(data *RPCData) bool {
return dim.ipNet.Contains(net.IP(net.ParseIP(data.DestinationAddr.String())))
func (dim *destinationIPMatcher) match(data *rpcData) bool {
return dim.ipNet.Contains(net.IP(net.ParseIP(data.destinationAddr.String())))
}

// portMatcher matches on whether the destination port of the RPC matches the
Expand All @@ -361,26 +361,56 @@ func newPortMatcher(destinationPort uint32) *portMatcher {
return &portMatcher{destinationPort: destinationPort}
}

func (pm *portMatcher) match(data *RPCData) bool {
return data.DestinationPort == pm.destinationPort
func (pm *portMatcher) match(data *rpcData) bool {
return data.destinationPort == pm.destinationPort
}

// authenticatedMatcher matches on the name of the Principal. If set, the URI
// SAN or DNS SAN in that order is used from the certificate, otherwise the
// subject field is used. If unset, it applies to any user that is
// authenticated. authenticatedMatcher implements the matcher interface.
type authenticatedMatcher struct {
stringMatcher internalmatcher.StringMatcher
stringMatcher *internalmatcher.StringMatcher
}

func newAuthenticatedMatcher(authenticatedMatcherConfig *v3rbacpb.Principal_Authenticated) (*authenticatedMatcher, error) {
// Represents this line in the RBAC documentation = "If unset, it applies to
// any user that is authenticated" (see package-level comments).
if authenticatedMatcherConfig.PrincipalName == nil {
return &authenticatedMatcher{}, nil
}
stringMatcher, err := internalmatcher.StringMatcherFromProto(authenticatedMatcherConfig.PrincipalName)
if err != nil {
return nil, err
}
return &authenticatedMatcher{stringMatcher: stringMatcher}, nil
return &authenticatedMatcher{stringMatcher: &stringMatcher}, nil
}

func (am *authenticatedMatcher) match(data *RPCData) bool {
return am.stringMatcher.Match(data.PrincipalName)
func (am *authenticatedMatcher) match(data *rpcData) bool {
// Represents this line in the RBAC documentation = "If unset, it applies to
// any user that is authenticated" (see package-level comments). An
// authenticated downstream in a stateful TLS connection will have to
// provide a certificate to prove their identity. Thus, you can simply check
// if there is a certificate present.
if am.stringMatcher == nil {
return len(data.certs) != 0
}
// No certificate present, so will never successfully match.
if len(data.certs) == 0 {
return false
}
cert := data.certs[0]
// The order of matching as per the RBAC documentation (see package-level comments)
// is as follows: URI SANs, DNS SANs, and then subject name.
for _, uriSAN := range cert.URIs {
if am.stringMatcher.Match(uriSAN.String()) {
return true
}
}
for _, dnsSAN := range cert.DNSNames {
if am.stringMatcher.Match(dnsSAN) {
return true
}
}
return am.stringMatcher.Match(cert.Subject.String())
}
Loading

0 comments on commit c513103

Please sign in to comment.