93611819e2
This is only configured in xDS when a service with an L7 protocol is exported. They also load any relevant trust bundles for the peered services to eventually use for L7 SPIFFE validation during mTLS termination.
162 lines
4.5 KiB
Go
162 lines
4.5 KiB
Go
package connect
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// CertURI represents a Connect-valid URI value for a TLS certificate.
|
|
// The user should type switch on the various implementations in this
|
|
// package to determine the type of URI and the data encoded within it.
|
|
//
|
|
// Note that the current implementations of this are all also SPIFFE IDs.
|
|
// 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 {
|
|
// URI is the valid URI value used in the cert.
|
|
URI() *url.URL
|
|
}
|
|
|
|
var (
|
|
spiffeIDServiceRegexp = regexp.MustCompile(
|
|
`^(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/]+)$`)
|
|
spiffeIDAgentRegexp = regexp.MustCompile(
|
|
`^(?:/ap/([^/]+))?/agent/client/dc/([^/]+)/id/([^/]+)$`)
|
|
spiffeIDMeshGatewayRegexp = regexp.MustCompile(
|
|
`^(?:/ap/([^/]+))?/gateway/mesh/dc/([^/]+)$`)
|
|
)
|
|
|
|
// ParseCertURIFromString attempts to parse a string representation of a
|
|
// certificate URI as a convenience helper around ParseCertURI.
|
|
func ParseCertURIFromString(input string) (CertURI, error) {
|
|
// Parse the certificate URI from the string
|
|
uriRaw, err := url.Parse(input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ParseCertURI(uriRaw)
|
|
}
|
|
|
|
// ParseCertURI parses a the URI value from a TLS certificate.
|
|
func ParseCertURI(input *url.URL) (CertURI, error) {
|
|
if input.Scheme != "spiffe" {
|
|
return nil, fmt.Errorf("SPIFFE ID must have 'spiffe' scheme")
|
|
}
|
|
|
|
// Path is the raw value of the path without url decoding values.
|
|
// RawPath is empty if there were no encoded values so we must
|
|
// check both.
|
|
path := input.Path
|
|
if input.RawPath != "" {
|
|
path = input.RawPath
|
|
}
|
|
|
|
// Test for service IDs
|
|
if v := spiffeIDServiceRegexp.FindStringSubmatch(path); v != nil {
|
|
// Determine the values. We assume they're reasonable to save cycles,
|
|
// but if the raw path is not empty that means that something is
|
|
// URL encoded so we go to the slow path.
|
|
ap := v[1]
|
|
ns := v[2]
|
|
dc := v[3]
|
|
service := v[4]
|
|
if input.RawPath != "" {
|
|
var err error
|
|
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)
|
|
}
|
|
if dc, err = url.PathUnescape(v[3]); err != nil {
|
|
return nil, fmt.Errorf("Invalid datacenter: %s", err)
|
|
}
|
|
if service, err = url.PathUnescape(v[4]); err != nil {
|
|
return nil, fmt.Errorf("Invalid service: %s", err)
|
|
}
|
|
}
|
|
|
|
if ap == "" {
|
|
ap = "default"
|
|
}
|
|
|
|
return &SpiffeIDService{
|
|
Host: input.Host,
|
|
Partition: ap,
|
|
Namespace: ns,
|
|
Datacenter: dc,
|
|
Service: service,
|
|
}, nil
|
|
} else if v := spiffeIDAgentRegexp.FindStringSubmatch(path); v != nil {
|
|
// Determine the values. We assume they're reasonable to save cycles,
|
|
// but if the raw path is not empty that means that something is
|
|
// URL encoded so we go to the slow path.
|
|
ap := v[1]
|
|
dc := v[2]
|
|
agent := v[3]
|
|
if input.RawPath != "" {
|
|
var err error
|
|
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)
|
|
}
|
|
if agent, err = url.PathUnescape(v[3]); err != nil {
|
|
return nil, fmt.Errorf("Invalid node: %s", err)
|
|
}
|
|
}
|
|
|
|
if ap == "" {
|
|
ap = "default"
|
|
}
|
|
|
|
return &SpiffeIDAgent{
|
|
Host: input.Host,
|
|
Partition: ap,
|
|
Datacenter: dc,
|
|
Agent: agent,
|
|
}, nil
|
|
} else if v := spiffeIDMeshGatewayRegexp.FindStringSubmatch(path); v != nil {
|
|
// Determine the values. We assume they're reasonable to save cycles,
|
|
// but if the raw path is not empty that means that something is
|
|
// URL encoded so we go to the slow path.
|
|
ap := v[1]
|
|
dc := v[2]
|
|
if input.RawPath != "" {
|
|
var err error
|
|
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)
|
|
}
|
|
}
|
|
|
|
if ap == "" {
|
|
ap = "default"
|
|
}
|
|
|
|
return &SpiffeIDMeshGateway{
|
|
Host: input.Host,
|
|
Partition: ap,
|
|
Datacenter: dc,
|
|
}, nil
|
|
}
|
|
|
|
// Test for signing ID
|
|
if input.Path == "" {
|
|
idx := strings.Index(input.Host, ".")
|
|
if idx > 0 {
|
|
return &SpiffeIDSigning{
|
|
ClusterID: input.Host[:idx],
|
|
Domain: input.Host[idx+1:],
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("SPIFFE ID is not in the expected format: %s", input.String())
|
|
}
|