b57cf27e8f
* trim carriage return from certificates when inserting rootCA in the inMemDB * format rootCA properly when returning the CA on the connect CA endpoint * Fix linter warnings * Fix providers to trim certs before returning it * trim newlines on write when possible * add changelog * make sure all provider return a trailing newline after the root and intermediate certs * Fix endpoint to return trailing new line * Fix failing test with vault provider * make test more robust * make sure all provider return a trailing newline after the leaf certs * Check for suffix before removing newline and use function * Add comment to consul provider * Update change log Co-authored-by: R.B. Boyer <4903+rboyer@users.noreply.github.com> * fix typo * simplify code callflow Co-authored-by: R.B. Boyer <4903+rboyer@users.noreply.github.com> * extract requireNewLine as shared func * remove dependency to testify in testing file * remove extra newline in vault provider * Add cert newline fix to envoy xds * remove new line from mock provider * Remove adding a new line from provider and fix it when the cert is read * Add a comment to explain the fix * Add missing for leaf certs * fix missing new line * fix missing new line in leaf certs * remove extra new line in test * updage changelog Co-authored-by: Daniel Nephin <dnephin@hashicorp.com> * fix in vault provider and when reading cache (RPC call) * fix AWS provider * fix failing test in the provider * remove comments and empty lines * add check for empty cert in test * fix linter warnings * add new line for leaf and private key * use string concat instead of Sprintf * fix new lines for leaf signing * preallocate slice and remove append * Add new line to `SignIntermediate` and `CrossSignCA` Co-authored-by: R.B. Boyer <4903+rboyer@users.noreply.github.com> Co-authored-by: Daniel Nephin <dnephin@hashicorp.com>
105 lines
3.1 KiB
Go
105 lines
3.1 KiB
Go
package ca
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/consul/agent/connect"
|
|
)
|
|
|
|
func validateSetIntermediate(
|
|
intermediatePEM, rootPEM string,
|
|
currentPrivateKey string, // optional
|
|
spiffeID *connect.SpiffeIDSigning,
|
|
) error {
|
|
// Get the key from the incoming intermediate cert so we can compare it
|
|
// to the currently stored key.
|
|
intermediate, err := connect.ParseCert(intermediatePEM)
|
|
if err != nil {
|
|
return fmt.Errorf("error parsing intermediate PEM: %v", err)
|
|
}
|
|
|
|
if currentPrivateKey != "" {
|
|
privKey, err := connect.ParseSigner(currentPrivateKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Compare the two keys to make sure they match.
|
|
b1, err := x509.MarshalPKIXPublicKey(intermediate.PublicKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b2, err := x509.MarshalPKIXPublicKey(privKey.Public())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(b1, b2) {
|
|
return fmt.Errorf("intermediate cert is for a different private key")
|
|
}
|
|
}
|
|
|
|
// Validate the remaining fields and make sure the intermediate validates against
|
|
// the given root cert.
|
|
if !intermediate.IsCA {
|
|
return fmt.Errorf("intermediate is not a CA certificate")
|
|
}
|
|
if uriCount := len(intermediate.URIs); uriCount != 1 {
|
|
return fmt.Errorf("incoming intermediate cert has unexpected number of URIs: %d", uriCount)
|
|
}
|
|
if got, want := intermediate.URIs[0].String(), spiffeID.URI().String(); got != want {
|
|
return fmt.Errorf("incoming cert URI %q does not match current URI: %q", got, want)
|
|
}
|
|
|
|
pool := x509.NewCertPool()
|
|
pool.AppendCertsFromPEM([]byte(rootPEM))
|
|
_, err = intermediate.Verify(x509.VerifyOptions{
|
|
Roots: pool,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("could not verify intermediate cert against root: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateSignIntermediate(csr *x509.CertificateRequest, spiffeID *connect.SpiffeIDSigning) error {
|
|
// We explicitly _don't_ require that the CSR has a valid SPIFFE signing URI
|
|
// SAN because AWS PCA doesn't let us set one :(. We need to relax it here
|
|
// otherwise it would be impossible to migrate from built-in provider to AWS
|
|
// in multiple DCs without downtime. Nothing in Connect actually checks that
|
|
// currently so this is OK for now but it's sad we have to break the SPIFFE
|
|
// spec for AWS sake. Hopefully they'll add that ability soon.
|
|
uriCount := len(csr.URIs)
|
|
if uriCount > 0 {
|
|
if uriCount != 1 {
|
|
return fmt.Errorf("incoming CSR has unexpected number of URIs: %d", uriCount)
|
|
}
|
|
certURI, err := connect.ParseCertURI(csr.URIs[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Verify that the trust domain is valid.
|
|
if !spiffeID.CanSign(certURI) {
|
|
return fmt.Errorf("incoming CSR domain %q is not valid for our domain %q",
|
|
certURI.URI().String(), spiffeID.URI().String())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EnsureTrailingNewline this is used to fix a case where the provider do not return a new line after
|
|
// the certificate as per the specification see GH-8178 for more context
|
|
func EnsureTrailingNewline(cert string) string {
|
|
if cert == "" {
|
|
return cert
|
|
}
|
|
if strings.HasSuffix(cert, "\n") {
|
|
return cert
|
|
}
|
|
return fmt.Sprintf("%s\n", cert)
|
|
}
|