2018-03-19 13:53:57 -07:00
|
|
|
package connect
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
|
|
|
"regexp"
|
2018-03-24 08:46:12 -10:00
|
|
|
"strings"
|
2018-03-19 13:53:57 -07:00
|
|
|
)
|
|
|
|
|
2018-03-24 08:39:43 -10:00
|
|
|
// 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 {
|
2018-03-25 14:39:18 -10:00
|
|
|
// URI is the valid URI value used in the cert.
|
2018-03-19 13:53:57 -07:00
|
|
|
URI() *url.URL
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
spiffeIDServiceRegexp = regexp.MustCompile(
|
2021-06-25 16:47:47 -05:00
|
|
|
`^(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/]+)$`)
|
2019-06-27 22:22:07 +02:00
|
|
|
spiffeIDAgentRegexp = regexp.MustCompile(
|
2021-06-25 16:47:47 -05:00
|
|
|
`^(?:/ap/([^/]+))?/agent/client/dc/([^/]+)/id/([^/]+)$`)
|
2018-03-19 13:53:57 -07:00
|
|
|
)
|
|
|
|
|
2018-10-03 19:18:55 +01:00
|
|
|
// ParseCertURIFromString attempts to parse a string representation of a
|
2019-03-06 11:13:28 -06:00
|
|
|
// certificate URI as a convenience helper around ParseCertURI.
|
2018-10-03 19:18:55 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2018-03-24 08:39:43 -10:00
|
|
|
// ParseCertURI parses a the URI value from a TLS certificate.
|
|
|
|
func ParseCertURI(input *url.URL) (CertURI, error) {
|
2018-03-19 13:53:57 -07:00
|
|
|
if input.Scheme != "spiffe" {
|
|
|
|
return nil, fmt.Errorf("SPIFFE ID must have 'spiffe' scheme")
|
|
|
|
}
|
|
|
|
|
2018-03-26 20:31:17 -07:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2018-03-19 13:53:57 -07:00
|
|
|
// Test for service IDs
|
2018-03-26 20:31:17 -07:00
|
|
|
if v := spiffeIDServiceRegexp.FindStringSubmatch(path); v != nil {
|
2021-07-02 12:18:46 -04:00
|
|
|
// Determine the values. We assume they're reasonable to save cycles,
|
2018-03-26 20:31:17 -07:00
|
|
|
// but if the raw path is not empty that means that something is
|
|
|
|
// URL encoded so we go to the slow path.
|
2021-06-25 16:47:47 -05:00
|
|
|
ap := v[1]
|
|
|
|
ns := v[2]
|
|
|
|
dc := v[3]
|
|
|
|
service := v[4]
|
2018-03-26 20:31:17 -07:00
|
|
|
if input.RawPath != "" {
|
|
|
|
var err error
|
2021-06-25 16:47:47 -05:00
|
|
|
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 {
|
2018-03-26 20:31:17 -07:00
|
|
|
return nil, fmt.Errorf("Invalid namespace: %s", err)
|
|
|
|
}
|
2021-06-25 16:47:47 -05:00
|
|
|
if dc, err = url.PathUnescape(v[3]); err != nil {
|
2018-03-26 20:31:17 -07:00
|
|
|
return nil, fmt.Errorf("Invalid datacenter: %s", err)
|
|
|
|
}
|
2021-06-25 16:47:47 -05:00
|
|
|
if service, err = url.PathUnescape(v[4]); err != nil {
|
2018-03-26 20:31:17 -07:00
|
|
|
return nil, fmt.Errorf("Invalid service: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-01 13:32:34 -06:00
|
|
|
if ap == "" {
|
|
|
|
ap = "default"
|
|
|
|
}
|
|
|
|
|
2018-03-19 13:53:57 -07:00
|
|
|
return &SpiffeIDService{
|
|
|
|
Host: input.Host,
|
2021-06-25 16:47:47 -05:00
|
|
|
Partition: ap,
|
2018-03-26 20:31:17 -07:00
|
|
|
Namespace: ns,
|
|
|
|
Datacenter: dc,
|
|
|
|
Service: service,
|
2018-03-19 13:53:57 -07:00
|
|
|
}, nil
|
2019-06-27 22:22:07 +02:00
|
|
|
} else if v := spiffeIDAgentRegexp.FindStringSubmatch(path); v != nil {
|
2021-07-02 12:18:46 -04:00
|
|
|
// Determine the values. We assume they're reasonable to save cycles,
|
2019-06-27 22:22:07 +02:00
|
|
|
// but if the raw path is not empty that means that something is
|
|
|
|
// URL encoded so we go to the slow path.
|
2021-06-25 16:47:47 -05:00
|
|
|
ap := v[1]
|
|
|
|
dc := v[2]
|
|
|
|
agent := v[3]
|
2019-06-27 22:22:07 +02:00
|
|
|
if input.RawPath != "" {
|
|
|
|
var err error
|
2021-06-25 16:47:47 -05:00
|
|
|
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 {
|
2019-06-27 22:22:07 +02:00
|
|
|
return nil, fmt.Errorf("Invalid datacenter: %s", err)
|
|
|
|
}
|
2021-06-25 16:47:47 -05:00
|
|
|
if agent, err = url.PathUnescape(v[3]); err != nil {
|
2019-06-27 22:22:07 +02:00
|
|
|
return nil, fmt.Errorf("Invalid node: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-01 13:32:34 -06:00
|
|
|
if ap == "" {
|
|
|
|
ap = "default"
|
|
|
|
}
|
|
|
|
|
2019-06-27 22:22:07 +02:00
|
|
|
return &SpiffeIDAgent{
|
|
|
|
Host: input.Host,
|
2021-06-25 16:47:47 -05:00
|
|
|
Partition: ap,
|
2019-06-27 22:22:07 +02:00
|
|
|
Datacenter: dc,
|
|
|
|
Agent: agent,
|
|
|
|
}, nil
|
2018-03-19 13:53:57 -07:00
|
|
|
}
|
|
|
|
|
2018-03-24 08:46:12 -10:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-27 22:22:07 +02:00
|
|
|
return nil, fmt.Errorf("SPIFFE ID is not in the expected format: %s", input.String())
|
2018-03-19 13:53:57 -07:00
|
|
|
}
|