connect: include optional partition prefixes in SPIFFE identifiers (#10507)
NOTE: this does not include any intentions enforcement changes yet
This commit is contained in:
parent
6adc615512
commit
30ccd5c2d9
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:feature
|
||||||
|
connect: include optional partition prefixes in SPIFFE identifiers
|
||||||
|
```
|
|
@ -216,6 +216,7 @@ func (ac *AutoConfig) generateCSR() (csr string, key string, err error) {
|
||||||
Host: unknownTrustDomain,
|
Host: unknownTrustDomain,
|
||||||
Datacenter: ac.config.Datacenter,
|
Datacenter: ac.config.Datacenter,
|
||||||
Agent: ac.config.NodeName,
|
Agent: ac.config.NodeName,
|
||||||
|
// TODO(rb)(partitions): populate the partition field from the agent config
|
||||||
}
|
}
|
||||||
|
|
||||||
caConfig, err := ac.config.ConnectCAConfiguration()
|
caConfig, err := ac.config.ConnectCAConfiguration()
|
||||||
|
|
|
@ -524,6 +524,7 @@ func (c *ConnectCALeaf) generateNewLeaf(req *ConnectCALeafRequest,
|
||||||
id = &connect.SpiffeIDService{
|
id = &connect.SpiffeIDService{
|
||||||
Host: roots.TrustDomain,
|
Host: roots.TrustDomain,
|
||||||
Datacenter: req.Datacenter,
|
Datacenter: req.Datacenter,
|
||||||
|
Partition: req.TargetPartition(),
|
||||||
Namespace: req.TargetNamespace(),
|
Namespace: req.TargetNamespace(),
|
||||||
Service: req.Service,
|
Service: req.Service,
|
||||||
}
|
}
|
||||||
|
@ -532,6 +533,7 @@ func (c *ConnectCALeaf) generateNewLeaf(req *ConnectCALeafRequest,
|
||||||
id = &connect.SpiffeIDAgent{
|
id = &connect.SpiffeIDAgent{
|
||||||
Host: roots.TrustDomain,
|
Host: roots.TrustDomain,
|
||||||
Datacenter: req.Datacenter,
|
Datacenter: req.Datacenter,
|
||||||
|
Partition: req.TargetPartition(),
|
||||||
Agent: req.Agent,
|
Agent: req.Agent,
|
||||||
}
|
}
|
||||||
dnsNames = append([]string{"localhost"}, req.DNSSAN...)
|
dnsNames = append([]string{"localhost"}, req.DNSSAN...)
|
||||||
|
@ -676,6 +678,10 @@ func (r *ConnectCALeafRequest) Key() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (req *ConnectCALeafRequest) TargetPartition() string {
|
||||||
|
return req.PartitionOrDefault()
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ConnectCALeafRequest) CacheInfo() cache.RequestInfo {
|
func (r *ConnectCALeafRequest) CacheInfo() cache.RequestInfo {
|
||||||
return cache.RequestInfo{
|
return cache.RequestInfo{
|
||||||
Token: r.Token,
|
Token: r.Token,
|
||||||
|
|
|
@ -21,9 +21,9 @@ type CertURI interface {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
spiffeIDServiceRegexp = regexp.MustCompile(
|
spiffeIDServiceRegexp = regexp.MustCompile(
|
||||||
`^/ns/([^/]+)/dc/([^/]+)/svc/([^/]+)$`)
|
`^(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/]+)$`)
|
||||||
spiffeIDAgentRegexp = regexp.MustCompile(
|
spiffeIDAgentRegexp = regexp.MustCompile(
|
||||||
`^/agent/client/dc/([^/]+)/id/([^/]+)$`)
|
`^(?:/ap/([^/]+))?/agent/client/dc/([^/]+)/id/([^/]+)$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseCertURIFromString attempts to parse a string representation of a
|
// ParseCertURIFromString attempts to parse a string representation of a
|
||||||
|
@ -56,24 +56,29 @@ func ParseCertURI(input *url.URL) (CertURI, error) {
|
||||||
// Determine the values. We assume they're sane to save cycles,
|
// Determine the values. We assume they're sane to save cycles,
|
||||||
// but if the raw path is not empty that means that something is
|
// but if the raw path is not empty that means that something is
|
||||||
// URL encoded so we go to the slow path.
|
// URL encoded so we go to the slow path.
|
||||||
ns := v[1]
|
ap := v[1]
|
||||||
dc := v[2]
|
ns := v[2]
|
||||||
service := v[3]
|
dc := v[3]
|
||||||
|
service := v[4]
|
||||||
if input.RawPath != "" {
|
if input.RawPath != "" {
|
||||||
var err error
|
var err error
|
||||||
if ns, err = url.PathUnescape(v[1]); err != nil {
|
if ap, err = url.PathUnescape(v[1]); err != nil {
|
||||||
|
return nil, fmt.Errorf("Invalid admin partition: %s", err)
|
||||||
|
}
|
||||||
|
if ns, err = url.PathUnescape(v[2]); err != nil {
|
||||||
return nil, fmt.Errorf("Invalid namespace: %s", err)
|
return nil, fmt.Errorf("Invalid namespace: %s", err)
|
||||||
}
|
}
|
||||||
if dc, err = url.PathUnescape(v[2]); err != nil {
|
if dc, err = url.PathUnescape(v[3]); err != nil {
|
||||||
return nil, fmt.Errorf("Invalid datacenter: %s", err)
|
return nil, fmt.Errorf("Invalid datacenter: %s", err)
|
||||||
}
|
}
|
||||||
if service, err = url.PathUnescape(v[3]); err != nil {
|
if service, err = url.PathUnescape(v[4]); err != nil {
|
||||||
return nil, fmt.Errorf("Invalid service: %s", err)
|
return nil, fmt.Errorf("Invalid service: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SpiffeIDService{
|
return &SpiffeIDService{
|
||||||
Host: input.Host,
|
Host: input.Host,
|
||||||
|
Partition: ap,
|
||||||
Namespace: ns,
|
Namespace: ns,
|
||||||
Datacenter: dc,
|
Datacenter: dc,
|
||||||
Service: service,
|
Service: service,
|
||||||
|
@ -82,20 +87,25 @@ func ParseCertURI(input *url.URL) (CertURI, error) {
|
||||||
// Determine the values. We assume they're sane to save cycles,
|
// Determine the values. We assume they're sane to save cycles,
|
||||||
// but if the raw path is not empty that means that something is
|
// but if the raw path is not empty that means that something is
|
||||||
// URL encoded so we go to the slow path.
|
// URL encoded so we go to the slow path.
|
||||||
dc := v[1]
|
ap := v[1]
|
||||||
agent := v[2]
|
dc := v[2]
|
||||||
|
agent := v[3]
|
||||||
if input.RawPath != "" {
|
if input.RawPath != "" {
|
||||||
var err error
|
var err error
|
||||||
if dc, err = url.PathUnescape(v[1]); err != nil {
|
if ap, err = url.PathUnescape(v[1]); err != nil {
|
||||||
|
return nil, fmt.Errorf("Invalid admin partition: %s", err)
|
||||||
|
}
|
||||||
|
if dc, err = url.PathUnescape(v[2]); err != nil {
|
||||||
return nil, fmt.Errorf("Invalid datacenter: %s", err)
|
return nil, fmt.Errorf("Invalid datacenter: %s", err)
|
||||||
}
|
}
|
||||||
if agent, err = url.PathUnescape(v[2]); err != nil {
|
if agent, err = url.PathUnescape(v[3]); err != nil {
|
||||||
return nil, fmt.Errorf("Invalid node: %s", err)
|
return nil, fmt.Errorf("Invalid node: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SpiffeIDAgent{
|
return &SpiffeIDAgent{
|
||||||
Host: input.Host,
|
Host: input.Host,
|
||||||
|
Partition: ap,
|
||||||
Datacenter: dc,
|
Datacenter: dc,
|
||||||
Agent: agent,
|
Agent: agent,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -1,22 +1,28 @@
|
||||||
package connect
|
package connect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SpiffeIDService is the structure to represent the SPIFFE ID for an agent.
|
// SpiffeIDService is the structure to represent the SPIFFE ID for an agent.
|
||||||
type SpiffeIDAgent struct {
|
type SpiffeIDAgent struct {
|
||||||
Host string
|
Host string
|
||||||
|
Partition string
|
||||||
Datacenter string
|
Datacenter string
|
||||||
Agent string
|
Agent string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (id SpiffeIDAgent) PartitionOrDefault() string {
|
||||||
|
return structs.PartitionOrDefault(id.Partition)
|
||||||
|
}
|
||||||
|
|
||||||
// URI returns the *url.URL for this SPIFFE ID.
|
// URI returns the *url.URL for this SPIFFE ID.
|
||||||
func (id *SpiffeIDAgent) URI() *url.URL {
|
func (id SpiffeIDAgent) URI() *url.URL {
|
||||||
var result url.URL
|
var result url.URL
|
||||||
result.Scheme = "spiffe"
|
result.Scheme = "spiffe"
|
||||||
result.Host = id.Host
|
result.Host = id.Host
|
||||||
result.Path = fmt.Sprintf("/agent/client/dc/%s/id/%s", id.Datacenter, id.Agent)
|
result.Path = id.uriPath()
|
||||||
return &result
|
return &result
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package connect
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func (id SpiffeIDAgent) uriPath() string {
|
||||||
|
return fmt.Sprintf("/agent/client/dc/%s/id/%s", id.Datacenter, id.Agent)
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package connect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSpiffeIDAgentURI(t *testing.T) {
|
||||||
|
t.Run("default partition", func(t *testing.T) {
|
||||||
|
agent := &SpiffeIDAgent{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Agent: "123",
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, "spiffe://1234.consul/agent/client/dc/dc1/id/123", agent.URI().String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("partitions are ignored", func(t *testing.T) {
|
||||||
|
agent := &SpiffeIDAgent{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Partition: "foobar",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Agent: "123",
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, "spiffe://1234.consul/agent/client/dc/dc1/id/123", agent.URI().String())
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,17 +0,0 @@
|
||||||
package connect
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSpiffeIDAgentURI(t *testing.T) {
|
|
||||||
agent := &SpiffeIDAgent{
|
|
||||||
Host: "1234.consul",
|
|
||||||
Datacenter: "dc1",
|
|
||||||
Agent: "123",
|
|
||||||
}
|
|
||||||
|
|
||||||
require.Equal(t, "spiffe://1234.consul/agent/client/dc/dc1/id/123", agent.URI().String())
|
|
||||||
}
|
|
|
@ -1,24 +1,37 @@
|
||||||
package connect
|
package connect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SpiffeIDService is the structure to represent the SPIFFE ID for a service.
|
// SpiffeIDService is the structure to represent the SPIFFE ID for a service.
|
||||||
type SpiffeIDService struct {
|
type SpiffeIDService struct {
|
||||||
Host string
|
Host string
|
||||||
|
Partition string
|
||||||
Namespace string
|
Namespace string
|
||||||
Datacenter string
|
Datacenter string
|
||||||
Service string
|
Service string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (id SpiffeIDService) NamespaceOrDefault() string {
|
||||||
|
return structs.NamespaceOrDefault(id.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id SpiffeIDService) MatchesPartition(partition string) bool {
|
||||||
|
return id.PartitionOrDefault() == structs.PartitionOrDefault(partition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id SpiffeIDService) PartitionOrDefault() string {
|
||||||
|
return structs.PartitionOrDefault(id.Partition)
|
||||||
|
}
|
||||||
|
|
||||||
// URI returns the *url.URL for this SPIFFE ID.
|
// URI returns the *url.URL for this SPIFFE ID.
|
||||||
func (id *SpiffeIDService) URI() *url.URL {
|
func (id SpiffeIDService) URI() *url.URL {
|
||||||
var result url.URL
|
var result url.URL
|
||||||
result.Scheme = "spiffe"
|
result.Scheme = "spiffe"
|
||||||
result.Host = id.Host
|
result.Host = id.Host
|
||||||
result.Path = fmt.Sprintf("/ns/%s/dc/%s/svc/%s",
|
result.Path = id.uriPath()
|
||||||
id.Namespace, id.Datacenter, id.Service)
|
|
||||||
return &result
|
return &result
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,21 @@
|
||||||
package connect
|
package connect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetEnterpriseMeta will synthesize an EnterpriseMeta struct from the SpiffeIDService.
|
// GetEnterpriseMeta will synthesize an EnterpriseMeta struct from the SpiffeIDService.
|
||||||
// in OSS this just returns an empty (but never nil) struct pointer
|
// in OSS this just returns an empty (but never nil) struct pointer
|
||||||
func (id *SpiffeIDService) GetEnterpriseMeta() *structs.EnterpriseMeta {
|
func (id SpiffeIDService) GetEnterpriseMeta() *structs.EnterpriseMeta {
|
||||||
return &structs.EnterpriseMeta{}
|
return &structs.EnterpriseMeta{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (id SpiffeIDService) uriPath() string {
|
||||||
|
return fmt.Sprintf("/ns/%s/dc/%s/svc/%s",
|
||||||
|
id.NamespaceOrDefault(),
|
||||||
|
id.Datacenter,
|
||||||
|
id.Service,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package connect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSpiffeIDServiceURI(t *testing.T) {
|
||||||
|
t.Run("default partition; default namespace", func(t *testing.T) {
|
||||||
|
svc := &SpiffeIDService{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Service: "web",
|
||||||
|
}
|
||||||
|
require.Equal(t, "spiffe://1234.consul/ns/default/dc/dc1/svc/web", svc.URI().String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("partitions are ignored", func(t *testing.T) {
|
||||||
|
svc := &SpiffeIDService{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Partition: "other",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Service: "web",
|
||||||
|
}
|
||||||
|
require.Equal(t, "spiffe://1234.consul/ns/default/dc/dc1/svc/web", svc.URI().String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("namespaces are ignored", func(t *testing.T) {
|
||||||
|
svc := &SpiffeIDService{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Namespace: "other",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Service: "web",
|
||||||
|
}
|
||||||
|
require.Equal(t, "spiffe://1234.consul/ns/default/dc/dc1/svc/web", svc.URI().String())
|
||||||
|
})
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ type SpiffeIDSigning struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// URI returns the *url.URL for this SPIFFE ID.
|
// URI returns the *url.URL for this SPIFFE ID.
|
||||||
func (id *SpiffeIDSigning) URI() *url.URL {
|
func (id SpiffeIDSigning) URI() *url.URL {
|
||||||
var result url.URL
|
var result url.URL
|
||||||
result.Scheme = "spiffe"
|
result.Scheme = "spiffe"
|
||||||
result.Host = id.Host()
|
result.Host = id.Host()
|
||||||
|
@ -24,7 +24,7 @@ func (id *SpiffeIDSigning) URI() *url.URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Host is the canonical representation as a DNS-compatible hostname.
|
// Host is the canonical representation as a DNS-compatible hostname.
|
||||||
func (id *SpiffeIDSigning) Host() string {
|
func (id SpiffeIDSigning) Host() string {
|
||||||
return strings.ToLower(fmt.Sprintf("%s.%s", id.ClusterID, id.Domain))
|
return strings.ToLower(fmt.Sprintf("%s.%s", id.ClusterID, id.Domain))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ func (id *SpiffeIDSigning) Host() string {
|
||||||
// method on CertURI interface since we don't intend this to be extensible
|
// method on CertURI interface since we don't intend this to be extensible
|
||||||
// outside and it's easier to reason about the security properties when they are
|
// outside and it's easier to reason about the security properties when they are
|
||||||
// all in one place with "allowlist" semantics.
|
// all in one place with "allowlist" semantics.
|
||||||
func (id *SpiffeIDSigning) CanSign(cu CertURI) bool {
|
func (id SpiffeIDSigning) CanSign(cu CertURI) bool {
|
||||||
switch other := cu.(type) {
|
switch other := cu.(type) {
|
||||||
case *SpiffeIDSigning:
|
case *SpiffeIDSigning:
|
||||||
// We can only sign other CA certificates for the same trust domain. Note
|
// We can only sign other CA certificates for the same trust domain. Note
|
||||||
|
|
|
@ -79,25 +79,25 @@ func TestSpiffeIDSigning_CanSign(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "service - good",
|
name: "service - good",
|
||||||
id: testSigning,
|
id: testSigning,
|
||||||
input: &SpiffeIDService{TestClusterID + ".consul", "default", "dc1", "web"},
|
input: &SpiffeIDService{Host: TestClusterID + ".consul", Namespace: "default", Datacenter: "dc1", Service: "web"},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "service - good midex case",
|
name: "service - good midex case",
|
||||||
id: testSigning,
|
id: testSigning,
|
||||||
input: &SpiffeIDService{strings.ToUpper(TestClusterID) + ".CONsuL", "defAUlt", "dc1", "WEB"},
|
input: &SpiffeIDService{Host: strings.ToUpper(TestClusterID) + ".CONsuL", Namespace: "defAUlt", Datacenter: "dc1", Service: "WEB"},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "service - different cluster",
|
name: "service - different cluster",
|
||||||
id: testSigning,
|
id: testSigning,
|
||||||
input: &SpiffeIDService{"55555555-4444-3333-2222-111111111111.consul", "default", "dc1", "web"},
|
input: &SpiffeIDService{Host: "55555555-4444-3333-2222-111111111111.consul", Namespace: "default", Datacenter: "dc1", Service: "web"},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "service - different TLD",
|
name: "service - different TLD",
|
||||||
id: testSigning,
|
id: testSigning,
|
||||||
input: &SpiffeIDService{TestClusterID + ".fake", "default", "dc1", "web"},
|
input: &SpiffeIDService{Host: TestClusterID + ".fake", Namespace: "default", Datacenter: "dc1", Service: "web"},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,86 +3,113 @@ package connect
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// testCertURICases contains the test cases for parsing and encoding
|
|
||||||
// the SPIFFE IDs. This is a global since it is used in multiple test functions.
|
|
||||||
var testCertURICases = []struct {
|
|
||||||
Name string
|
|
||||||
URI string
|
|
||||||
Struct interface{}
|
|
||||||
ParseError string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"invalid scheme",
|
|
||||||
"http://google.com/",
|
|
||||||
nil,
|
|
||||||
"scheme",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"basic service ID",
|
|
||||||
"spiffe://1234.consul/ns/default/dc/dc01/svc/web",
|
|
||||||
&SpiffeIDService{
|
|
||||||
Host: "1234.consul",
|
|
||||||
Namespace: "default",
|
|
||||||
Datacenter: "dc01",
|
|
||||||
Service: "web",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"basic agent ID",
|
|
||||||
"spiffe://1234.consul/agent/client/dc/dc1/id/uuid",
|
|
||||||
&SpiffeIDAgent{
|
|
||||||
Host: "1234.consul",
|
|
||||||
Datacenter: "dc1",
|
|
||||||
Agent: "uuid",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"service with URL-encoded values",
|
|
||||||
"spiffe://1234.consul/ns/foo%2Fbar/dc/bar%2Fbaz/svc/baz%2Fqux",
|
|
||||||
&SpiffeIDService{
|
|
||||||
Host: "1234.consul",
|
|
||||||
Namespace: "foo/bar",
|
|
||||||
Datacenter: "bar/baz",
|
|
||||||
Service: "baz/qux",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"signing ID",
|
|
||||||
"spiffe://1234.consul",
|
|
||||||
&SpiffeIDSigning{
|
|
||||||
ClusterID: "1234",
|
|
||||||
Domain: "consul",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseCertURIFromString(t *testing.T) {
|
func TestParseCertURIFromString(t *testing.T) {
|
||||||
for _, tc := range testCertURICases {
|
var cases = []struct {
|
||||||
t.Run(tc.Name, func(t *testing.T) {
|
Name string
|
||||||
assert := assert.New(t)
|
URI string
|
||||||
|
Struct interface{}
|
||||||
|
ParseError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"invalid scheme",
|
||||||
|
"http://google.com/",
|
||||||
|
nil,
|
||||||
|
"scheme",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basic service ID",
|
||||||
|
"spiffe://1234.consul/ns/default/dc/dc01/svc/web",
|
||||||
|
&SpiffeIDService{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Namespace: "default",
|
||||||
|
Datacenter: "dc01",
|
||||||
|
Service: "web",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basic service ID with partition",
|
||||||
|
"spiffe://1234.consul/ap/bizdev/ns/default/dc/dc01/svc/web",
|
||||||
|
&SpiffeIDService{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Partition: "bizdev",
|
||||||
|
Namespace: "default",
|
||||||
|
Datacenter: "dc01",
|
||||||
|
Service: "web",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basic agent ID",
|
||||||
|
"spiffe://1234.consul/agent/client/dc/dc1/id/uuid",
|
||||||
|
&SpiffeIDAgent{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Agent: "uuid",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basic agent ID with partition",
|
||||||
|
"spiffe://1234.consul/ap/bizdev/agent/client/dc/dc1/id/uuid",
|
||||||
|
&SpiffeIDAgent{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Partition: "bizdev",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Agent: "uuid",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service with URL-encoded values",
|
||||||
|
"spiffe://1234.consul/ns/foo%2Fbar/dc/bar%2Fbaz/svc/baz%2Fqux",
|
||||||
|
&SpiffeIDService{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Namespace: "foo/bar",
|
||||||
|
Datacenter: "bar/baz",
|
||||||
|
Service: "baz/qux",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service with URL-encoded values with partition",
|
||||||
|
"spiffe://1234.consul/ap/biz%2Fdev/ns/foo%2Fbar/dc/bar%2Fbaz/svc/baz%2Fqux",
|
||||||
|
&SpiffeIDService{
|
||||||
|
Host: "1234.consul",
|
||||||
|
Partition: "biz/dev",
|
||||||
|
Namespace: "foo/bar",
|
||||||
|
Datacenter: "bar/baz",
|
||||||
|
Service: "baz/qux",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signing ID",
|
||||||
|
"spiffe://1234.consul",
|
||||||
|
&SpiffeIDSigning{
|
||||||
|
ClusterID: "1234",
|
||||||
|
Domain: "consul",
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the ID and check the error/return value
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
actual, err := ParseCertURIFromString(tc.URI)
|
actual, err := ParseCertURIFromString(tc.URI)
|
||||||
if err != nil {
|
if tc.ParseError != "" {
|
||||||
t.Logf("parse error: %s", err.Error())
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), tc.ParseError)
|
||||||
|
testutil.RequireErrorContains(t, err, tc.ParseError)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.Struct, actual)
|
||||||
}
|
}
|
||||||
assert.Equal(tc.ParseError != "", err != nil, "error value")
|
|
||||||
if err != nil {
|
|
||||||
assert.Contains(err.Error(), tc.ParseError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assert.Equal(tc.Struct, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package agent
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/agent/cache"
|
"github.com/hashicorp/consul/agent/cache"
|
||||||
cachetype "github.com/hashicorp/consul/agent/cache-types"
|
cachetype "github.com/hashicorp/consul/agent/cache-types"
|
||||||
|
@ -68,6 +69,13 @@ func (a *Agent) ConnectAuthorize(token string,
|
||||||
return returnErr(acl.ErrPermissionDenied)
|
return returnErr(acl.ErrPermissionDenied)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !uriService.MatchesPartition(req.TargetPartition()) {
|
||||||
|
reason = fmt.Sprintf("Mismatched partitions: %q != %q",
|
||||||
|
uriService.PartitionOrDefault(),
|
||||||
|
structs.PartitionOrDefault(req.TargetPartition()))
|
||||||
|
return false, reason, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Note that we DON'T explicitly validate the trust-domain matches ours. See
|
// Note that we DON'T explicitly validate the trust-domain matches ours. See
|
||||||
// the PR for this change for details.
|
// the PR for this change for details.
|
||||||
|
|
||||||
|
|
|
@ -18,3 +18,7 @@ type ConnectAuthorizeRequest struct {
|
||||||
ClientCertURI string
|
ClientCertURI string
|
||||||
ClientCertSerial string
|
ClientCertSerial string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (req *ConnectAuthorizeRequest) TargetPartition() string {
|
||||||
|
return req.PartitionOrDefault()
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,10 @@ func (m *EnterpriseMeta) NamespaceOrDefault() string {
|
||||||
return IntentionDefaultNamespace
|
return IntentionDefaultNamespace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NamespaceOrDefault(_ string) string {
|
||||||
|
return IntentionDefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
func (m *EnterpriseMeta) NamespaceOrEmpty() string {
|
func (m *EnterpriseMeta) NamespaceOrEmpty() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -54,6 +58,10 @@ func (m *EnterpriseMeta) PartitionOrDefault() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PartitionOrDefault(_ string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (m *EnterpriseMeta) PartitionOrEmpty() string {
|
func (m *EnterpriseMeta) PartitionOrEmpty() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,7 @@ func (cr *ConsulResolver) resolveServiceEntry(entry *api.ServiceEntry) (string,
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
Datacenter: entry.Node.Datacenter,
|
Datacenter: entry.Node.Datacenter,
|
||||||
Service: service,
|
Service: service,
|
||||||
|
// NOTE: this only handles the default implicit partition currently.
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipaddr.FormatAddressPort(addr, port), certURI, nil
|
return ipaddr.FormatAddressPort(addr, port), certURI, nil
|
||||||
|
|
Loading…
Reference in New Issue