c3d5a2a5ab
As part of this change, we ensure that the SAN extensions are marked as critical when the subject is empty so that AWS PCA tolerates the loss of common names well and continues to function as a Connect CA provider. Parts of this currently hack around a bug in crypto/x509 and can be removed after https://go-review.googlesource.com/c/go/+/329129 lands in a Go release. Note: the AWS PCA tests do not run automatically, but the following passed locally for me: ENABLE_AWS_PCA_TESTS=1 go test ./agent/connect/ca -run TestAWS
70 lines
2.4 KiB
Go
70 lines
2.4 KiB
Go
package connect
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var invalidDNSNameChars = regexp.MustCompile(`[^a-z0-9]`)
|
|
|
|
// CompactUID returns a crypto random Unique Identifier string consiting of 8
|
|
// characters of base36 encoded random value. This has roughly 41 bits of
|
|
// entropy so is suitable for infrequently occuring events with low probability
|
|
// of collision. It is not suitable for UUIDs for very frequent events. It's
|
|
// main purpose is to assign unique values to CA certificate Common Names which
|
|
// need to be unique in some providers - see CACN - but without using up large
|
|
// amounts of the limited 64 character Common Name. It also makes the values
|
|
// more easily digestable by humans considering there are likely to be few of
|
|
// them ever in use.
|
|
func CompactUID() (string, error) {
|
|
// 48 bits (6 bytes) is enough to fill 8 bytes in base36 but it's simpler to
|
|
// have a whole uint8 to convert from.
|
|
var raw [8]byte
|
|
_, err := rand.Read(raw[:])
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
i := binary.LittleEndian.Uint64(raw[:])
|
|
return truncateTo(strconv.FormatInt(int64(i), 36), 8), nil
|
|
}
|
|
|
|
// CACN returns the common name for a CA certificate.
|
|
// A uniqueID is requires because some providers (e.g.
|
|
// Vault) cache by subject and so produce incorrect results - for example they
|
|
// won't cross-sign an older CA certificate with the same common name since they
|
|
// think they already have a valid cert for that CN and just return the current
|
|
// root.
|
|
//
|
|
// This can be generated by any means but will be truncated to 8 chars and
|
|
// sanitised to DNS-safe chars. CompactUID generates suitable UIDs for this
|
|
// specific purpose.
|
|
//
|
|
// Format is:
|
|
// {provider}-{uniqueID_first8}.{pri|sec}.ca.<trust_domain_first_8>.consul
|
|
//
|
|
// trust domain is truncated to keep the whole name short
|
|
func CACN(provider, uniqueID, trustDomain string, primaryDC bool) string {
|
|
providerSan := invalidDNSNameChars.ReplaceAllString(strings.ToLower(provider), "")
|
|
typ := "pri"
|
|
if !primaryDC {
|
|
typ = "sec"
|
|
}
|
|
// 32 = 7 bytes for ".consul", 8 bytes for trust domain, 8 bytes for
|
|
// ".pri.ca.", 9 bytes for "-{uniqueID-8-b36}"
|
|
uidSAN := invalidDNSNameChars.ReplaceAllString(strings.ToLower(uniqueID), "")
|
|
return fmt.Sprintf("%s-%s.%s.ca.%s.consul", typ, truncateTo(uidSAN, 8),
|
|
truncateTo(providerSan, 64-32), truncateTo(trustDomain, 8))
|
|
}
|
|
|
|
func truncateTo(s string, n int) string {
|
|
if len(s) > n {
|
|
return s[:n]
|
|
}
|
|
return s
|
|
}
|