agent/connect: Authorize for CertURI
This commit is contained in:
parent
68fa4a83b1
commit
7af99667b6
|
@ -5,6 +5,8 @@ import (
|
|||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
||||
// CertURI represents a Connect-valid URI value for a TLS certificate.
|
||||
|
@ -15,6 +17,14 @@ 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
|
||||
}
|
||||
|
||||
|
@ -79,36 +89,3 @@ func ParseCertURI(input *url.URL) (CertURI, error) {
|
|||
|
||||
return nil, fmt.Errorf("SPIFFE ID is not in the expected format")
|
||||
}
|
||||
|
||||
// SpiffeIDService is the structure to represent the SPIFFE ID for a service.
|
||||
type SpiffeIDService struct {
|
||||
Host string
|
||||
Namespace string
|
||||
Datacenter string
|
||||
Service string
|
||||
}
|
||||
|
||||
// URI returns the *url.URL for this SPIFFE ID.
|
||||
func (id *SpiffeIDService) URI() *url.URL {
|
||||
var result url.URL
|
||||
result.Scheme = "spiffe"
|
||||
result.Host = id.Host
|
||||
result.Path = fmt.Sprintf("/ns/%s/dc/%s/svc/%s",
|
||||
id.Namespace, id.Datacenter, id.Service)
|
||||
return &result
|
||||
}
|
||||
|
||||
// SpiffeIDSigning is the structure to represent the SPIFFE ID for a
|
||||
// signing certificate (not a leaf service).
|
||||
type SpiffeIDSigning struct {
|
||||
ClusterID string // Unique cluster ID
|
||||
Domain string // The domain, usually "consul"
|
||||
}
|
||||
|
||||
// URI returns the *url.URL for this SPIFFE ID.
|
||||
func (id *SpiffeIDSigning) URI() *url.URL {
|
||||
var result url.URL
|
||||
result.Scheme = "spiffe"
|
||||
result.Host = fmt.Sprintf("%s.%s", id.ClusterID, id.Domain)
|
||||
return &result
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package connect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
||||
// SpiffeIDService is the structure to represent the SPIFFE ID for a service.
|
||||
type SpiffeIDService struct {
|
||||
Host string
|
||||
Namespace string
|
||||
Datacenter string
|
||||
Service string
|
||||
}
|
||||
|
||||
// URI returns the *url.URL for this SPIFFE ID.
|
||||
func (id *SpiffeIDService) URI() *url.URL {
|
||||
var result url.URL
|
||||
result.Scheme = "spiffe"
|
||||
result.Host = id.Host
|
||||
result.Path = fmt.Sprintf("/ns/%s/dc/%s/svc/%s",
|
||||
id.Namespace, id.Datacenter, id.Service)
|
||||
return &result
|
||||
}
|
||||
|
||||
// CertURI impl.
|
||||
func (id *SpiffeIDService) Authorize(ixn *structs.Intention) (bool, bool) {
|
||||
if ixn.SourceNS != structs.IntentionWildcard && ixn.SourceNS != id.Namespace {
|
||||
// Non-matching namespace
|
||||
return false, false
|
||||
}
|
||||
|
||||
if ixn.SourceName != structs.IntentionWildcard && ixn.SourceName != id.Service {
|
||||
// Non-matching name
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Match, return allow value
|
||||
return ixn.Action == structs.IntentionActionAllow, true
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package connect
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSpiffeIDServiceAuthorize(t *testing.T) {
|
||||
ns := structs.IntentionDefaultNamespace
|
||||
serviceWeb := &SpiffeIDService{
|
||||
Host: "1234.consul",
|
||||
Namespace: structs.IntentionDefaultNamespace,
|
||||
Datacenter: "dc01",
|
||||
Service: "web",
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
URI *SpiffeIDService
|
||||
Ixn *structs.Intention
|
||||
Auth bool
|
||||
Match bool
|
||||
}{
|
||||
{
|
||||
"exact source, not matching namespace",
|
||||
serviceWeb,
|
||||
&structs.Intention{
|
||||
SourceNS: "different",
|
||||
SourceName: "db",
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"exact source, not matching name",
|
||||
serviceWeb,
|
||||
&structs.Intention{
|
||||
SourceNS: ns,
|
||||
SourceName: "db",
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"exact source, allow",
|
||||
serviceWeb,
|
||||
&structs.Intention{
|
||||
SourceNS: serviceWeb.Namespace,
|
||||
SourceName: serviceWeb.Service,
|
||||
Action: structs.IntentionActionAllow,
|
||||
},
|
||||
true,
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"exact source, deny",
|
||||
serviceWeb,
|
||||
&structs.Intention{
|
||||
SourceNS: serviceWeb.Namespace,
|
||||
SourceName: serviceWeb.Service,
|
||||
Action: structs.IntentionActionDeny,
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"exact namespace, wildcard service, deny",
|
||||
serviceWeb,
|
||||
&structs.Intention{
|
||||
SourceNS: serviceWeb.Namespace,
|
||||
SourceName: structs.IntentionWildcard,
|
||||
Action: structs.IntentionActionDeny,
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"exact namespace, wildcard service, allow",
|
||||
serviceWeb,
|
||||
&structs.Intention{
|
||||
SourceNS: serviceWeb.Namespace,
|
||||
SourceName: structs.IntentionWildcard,
|
||||
Action: structs.IntentionActionAllow,
|
||||
},
|
||||
true,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
auth, match := tc.URI.Authorize(tc.Ixn)
|
||||
assert.Equal(t, tc.Auth, auth)
|
||||
assert.Equal(t, tc.Match, match)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package connect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
||||
// SpiffeIDSigning is the structure to represent the SPIFFE ID for a
|
||||
// signing certificate (not a leaf service).
|
||||
type SpiffeIDSigning struct {
|
||||
ClusterID string // Unique cluster ID
|
||||
Domain string // The domain, usually "consul"
|
||||
}
|
||||
|
||||
// URI returns the *url.URL for this SPIFFE ID.
|
||||
func (id *SpiffeIDSigning) URI() *url.URL {
|
||||
var result url.URL
|
||||
result.Scheme = "spiffe"
|
||||
result.Host = fmt.Sprintf("%s.%s", id.ClusterID, id.Domain)
|
||||
return &result
|
||||
}
|
||||
|
||||
// CertURI impl.
|
||||
func (id *SpiffeIDSigning) Authorize(ixn *structs.Intention) (bool, bool) {
|
||||
// Never authorize as a client.
|
||||
return false, true
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package connect
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Signing ID should never authorize
|
||||
func TestSpiffeIDSigningAuthorize(t *testing.T) {
|
||||
var id SpiffeIDSigning
|
||||
auth, ok := id.Authorize(nil)
|
||||
assert.False(t, auth)
|
||||
assert.True(t, ok)
|
||||
}
|
Loading…
Reference in New Issue