Skip to content

Commit

Permalink
Add internal RPC endpoint to compute upstreams from intentions
Browse files Browse the repository at this point in the history
  • Loading branch information
freddygv authored Mar 17, 2021
2 parents 8207b83 + 9bff39b commit eca45f1
Show file tree
Hide file tree
Showing 18 changed files with 1,123 additions and 250 deletions.
49 changes: 49 additions & 0 deletions agent/connect/authz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package connect

import (
"github.com/hashicorp/consul/agent/structs"
)

// AuthorizeIntentionTarget determines whether the destination is covered by the given intention
// and whether the intention action allows a connection.
// This is a generalized version of the old CertURI.Authorize(), and can be evaluated against sources or destinations.
//
// The return value of `auth` is only valid if the second value `match` is true.
// If `match` is false, then the intention doesn't match this target and any result should be ignored.
func AuthorizeIntentionTarget(
target, targetNS string,
ixn *structs.Intention,
matchType structs.IntentionMatchType,
) (auth bool, match bool) {

switch matchType {
case structs.IntentionMatchDestination:
if ixn.DestinationNS != structs.WildcardSpecifier && ixn.DestinationNS != targetNS {
// Non-matching namespace
return false, false
}

if ixn.DestinationName != structs.WildcardSpecifier && ixn.DestinationName != target {
// Non-matching name
return false, false
}

case structs.IntentionMatchSource:
if ixn.SourceNS != structs.WildcardSpecifier && ixn.SourceNS != targetNS {
// Non-matching namespace
return false, false
}

if ixn.SourceName != structs.WildcardSpecifier && ixn.SourceName != target {
// Non-matching name
return false, false
}

default:
// Reject on any un-recognized match type
return false, false
}

// The name and namespace match, so the destination is covered
return ixn.Action == structs.IntentionActionAllow, true
}
196 changes: 196 additions & 0 deletions agent/connect/authz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package connect

import (
"github.com/hashicorp/consul/agent/structs"
"github.com/stretchr/testify/assert"
"testing"
)

func TestAuthorizeIntentionTarget(t *testing.T) {
cases := []struct {
name string
target string
targetNS string
ixn *structs.Intention
matchType structs.IntentionMatchType
auth bool
match bool
}{
// Source match type
{
name: "match exact source, not matching namespace",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
SourceName: "db",
SourceNS: "different",
},
matchType: structs.IntentionMatchSource,
auth: false,
match: false,
},
{
name: "match exact source, not matching name",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
SourceName: "db",
SourceNS: structs.IntentionDefaultNamespace,
},
matchType: structs.IntentionMatchSource,
auth: false,
match: false,
},
{
name: "match exact source, allow",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
SourceName: "web",
SourceNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionAllow,
},
matchType: structs.IntentionMatchSource,
auth: true,
match: true,
},
{
name: "match exact source, deny",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
SourceName: "web",
SourceNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionDeny,
},
matchType: structs.IntentionMatchSource,
auth: false,
match: true,
},
{
name: "match exact sourceNS for wildcard service, deny",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
SourceName: structs.WildcardSpecifier,
SourceNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionDeny,
},
matchType: structs.IntentionMatchSource,
auth: false,
match: true,
},
{
name: "match exact sourceNS for wildcard service, allow",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
SourceName: structs.WildcardSpecifier,
SourceNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionAllow,
},
matchType: structs.IntentionMatchSource,
auth: true,
match: true,
},

// Destination match type
{
name: "match exact destination, not matching namespace",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
DestinationName: "db",
DestinationNS: "different",
},
matchType: structs.IntentionMatchDestination,
auth: false,
match: false,
},
{
name: "match exact destination, not matching name",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
DestinationName: "db",
DestinationNS: structs.IntentionDefaultNamespace,
},
matchType: structs.IntentionMatchDestination,
auth: false,
match: false,
},
{
name: "match exact destination, allow",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
DestinationName: "web",
DestinationNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionAllow,
},
matchType: structs.IntentionMatchDestination,
auth: true,
match: true,
},
{
name: "match exact destination, deny",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
DestinationName: "web",
DestinationNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionDeny,
},
matchType: structs.IntentionMatchDestination,
auth: false,
match: true,
},
{
name: "match exact destinationNS for wildcard service, deny",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
DestinationName: structs.WildcardSpecifier,
DestinationNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionDeny,
},
matchType: structs.IntentionMatchDestination,
auth: false,
match: true,
},
{
name: "match exact destinationNS for wildcard service, allow",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
DestinationName: structs.WildcardSpecifier,
DestinationNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionAllow,
},
matchType: structs.IntentionMatchDestination,
auth: true,
match: true,
},
{
name: "unknown match type",
target: "web",
targetNS: structs.IntentionDefaultNamespace,
ixn: &structs.Intention{
DestinationName: structs.WildcardSpecifier,
DestinationNS: structs.IntentionDefaultNamespace,
Action: structs.IntentionActionAllow,
},
matchType: structs.IntentionMatchType("unknown"),
auth: false,
match: false,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
auth, match := AuthorizeIntentionTarget(tc.target, tc.targetNS, tc.ixn, tc.matchType)
assert.Equal(t, tc.auth, auth)
assert.Equal(t, tc.match, match)
})
}
}
9 changes: 0 additions & 9 deletions agent/connect/uri.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"net/url"
"regexp"
"strings"

"github.com/hashicorp/consul/agent/structs"
)

// CertURI represents a Connect-valid URI value for a TLS certificate.
Expand All @@ -17,13 +15,6 @@ import (
// However, we anticipate that we may accept URIs that are also not SPIFFE
// compliant and therefore the interface is named as such.
type CertURI interface {
// Authorize tests the authorization for this URI as a client
// for the given intention. The return value `auth` is only valid if
// the second value `match` is true. If the second value `match` is
// false, then the intention doesn't match this client and any
// result should be ignored.
Authorize(*structs.Intention) (auth bool, match bool)

// URI is the valid URI value used in the cert.
URI() *url.URL
}
Expand Down
7 changes: 0 additions & 7 deletions agent/connect/uri_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package connect
import (
"fmt"
"net/url"

"github.com/hashicorp/consul/agent/structs"
)

// SpiffeIDService is the structure to represent the SPIFFE ID for an agent.
Expand All @@ -23,11 +21,6 @@ func (id *SpiffeIDAgent) URI() *url.URL {
return &result
}

// CertURI impl.
func (id *SpiffeIDAgent) Authorize(_ *structs.Intention) (bool, bool) {
return false, false
}

func (id *SpiffeIDAgent) CommonName() string {
return AgentCN(id.Agent, id.Host)
}
11 changes: 0 additions & 11 deletions agent/connect/uri_agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,3 @@ func TestSpiffeIDAgentURI(t *testing.T) {

require.Equal(t, "spiffe://1234.consul/agent/client/dc/dc1/id/123", agent.URI().String())
}

func TestSpiffeIDAgentAuthorize(t *testing.T) {
agent := &SpiffeIDAgent{
Host: "1234.consul",
Agent: "uuid",
}

auth, match := agent.Authorize(nil)
require.False(t, auth)
require.False(t, match)
}
18 changes: 0 additions & 18 deletions agent/connect/uri_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package connect
import (
"fmt"
"net/url"

"github.com/hashicorp/consul/agent/structs"
)

// SpiffeIDService is the structure to represent the SPIFFE ID for a service.
Expand All @@ -25,22 +23,6 @@ func (id *SpiffeIDService) URI() *url.URL {
return &result
}

// CertURI impl.
func (id *SpiffeIDService) Authorize(ixn *structs.Intention) (bool, bool) {
if ixn.SourceNS != structs.WildcardSpecifier && ixn.SourceNS != id.Namespace {
// Non-matching namespace
return false, false
}

if ixn.SourceName != structs.WildcardSpecifier && ixn.SourceName != id.Service {
// Non-matching name
return false, false
}

// Match, return allow value
return ixn.Action == structs.IntentionActionAllow, true
}

func (id *SpiffeIDService) CommonName() string {
return ServiceCN(id.Service, id.Namespace, id.Host)
}
Loading

0 comments on commit eca45f1

Please sign in to comment.