diff --git a/agent/connect/ca.go b/agent/connect/ca.go index 87b01994ef..ff0f0813db 100644 --- a/agent/connect/ca.go +++ b/agent/connect/ca.go @@ -1,7 +1,6 @@ package connect import ( - "bytes" "crypto" "crypto/ecdsa" "crypto/rsa" @@ -28,21 +27,21 @@ func ParseCert(pemValue string) (*x509.Certificate, error) { return x509.ParseCertificate(block.Bytes) } -// ParseCertFingerprint parses the x509 certificate from a PEM-encoded value -// and returns the SHA-1 fingerprint. -func ParseCertFingerprint(pemValue string) (string, error) { +// CalculateCertFingerprint parses the x509 certificate from a PEM-encoded value +// and calculates the SHA-1 fingerprint. +func CalculateCertFingerprint(pemValue string) (string, error) { // The _ result below is not an error but the remaining PEM bytes. block, _ := pem.Decode([]byte(pemValue)) if block == nil { return "", fmt.Errorf("no PEM-encoded data found") } - hash := sha1.Sum(block.Bytes) - hexified := make([][]byte, len(hash)) - for i, data := range hash { - hexified[i] = []byte(fmt.Sprintf("%02X", data)) + if block.Type != "CERTIFICATE" { + return "", fmt.Errorf("first PEM-block should be CERTIFICATE type") } - return string(bytes.Join(hexified, []byte(":"))), nil + + hash := sha1.Sum(block.Bytes) + return HexString(hash[:]), nil } // ParseSigner parses a crypto.Signer from a PEM-encoded key. The private key diff --git a/agent/consul/connect_ca_endpoint.go b/agent/consul/connect_ca_endpoint.go index f52c9218e4..72ac2adbce 100644 --- a/agent/consul/connect_ca_endpoint.go +++ b/agent/consul/connect_ca_endpoint.go @@ -98,7 +98,7 @@ func (s *ConnectCA) ConfigurationSet( return err } - id, err := connect.ParseCertFingerprint(newRootPEM) + id, err := connect.CalculateCertFingerprint(newRootPEM) if err != nil { return fmt.Errorf("error parsing root fingerprint: %v", err) } diff --git a/agent/consul/connect_ca_provider.go b/agent/consul/connect_ca_provider.go index f9321138b2..afb74fe78b 100644 --- a/agent/consul/connect_ca_provider.go +++ b/agent/consul/connect_ca_provider.go @@ -222,7 +222,7 @@ func (c *ConsulCAProvider) Sign(csr *x509.CertificateRequest) (string, error) { // Cert template for generation sn := &big.Int{} - sn.SetUint64(providerState.LeafIndex + 1) + sn.SetUint64(providerState.SerialIndex + 1) template := x509.Certificate{ SerialNumber: sn, Subject: pkix.Name{CommonName: serviceId.Service}, @@ -240,6 +240,7 @@ func (c *ConsulCAProvider) Sign(csr *x509.CertificateRequest) (string, error) { x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth, }, + // todo(kyhavlov): add a way to set the cert lifetime here from the CA config NotAfter: time.Now().Add(3 * 24 * time.Hour), NotBefore: time.Now(), AuthorityKeyId: keyId, @@ -258,20 +259,7 @@ func (c *ConsulCAProvider) Sign(csr *x509.CertificateRequest) (string, error) { return "", fmt.Errorf("error encoding private key: %s", err) } - // Increment the leaf cert index - newState := *providerState - newState.LeafIndex += 1 - args := &structs.CARequest{ - Op: structs.CAOpSetProviderState, - ProviderState: &newState, - } - resp, err := c.srv.raftApply(structs.ConnectCARequestType, args) - if err != nil { - return "", err - } - if respErr, ok := resp.(error); ok { - return "", respErr - } + c.incrementSerialIndex(providerState) // Set the response return buf.String(), nil @@ -306,10 +294,9 @@ func (c *ConsulCAProvider) CrossSignCA(cert *x509.Certificate) (string, error) { // Create the cross-signing template from the existing root CA serialNum := &big.Int{} - serialNum.SetUint64(providerState.LeafIndex + 1) + serialNum.SetUint64(providerState.SerialIndex + 1) template := *cert template.SerialNumber = serialNum - template.Subject = rootCA.Subject template.SignatureAlgorithm = rootCA.SignatureAlgorithm template.SubjectKeyId = keyId template.AuthorityKeyId = keyId @@ -326,9 +313,30 @@ func (c *ConsulCAProvider) CrossSignCA(cert *x509.Certificate) (string, error) { return "", fmt.Errorf("error encoding private key: %s", err) } + c.incrementSerialIndex(providerState) + return buf.String(), nil } +// incrementSerialIndex increments the cert serial number index in the provider state +func (c *ConsulCAProvider) incrementSerialIndex(providerState *structs.CAConsulProviderState) error { + newState := *providerState + newState.SerialIndex += 1 + args := &structs.CARequest{ + Op: structs.CAOpSetProviderState, + ProviderState: &newState, + } + resp, err := c.srv.raftApply(structs.ConnectCARequestType, args) + if err != nil { + return err + } + if respErr, ok := resp.(error); ok { + return respErr + } + + return nil +} + // generatePrivateKey returns a new private key func generatePrivateKey() (string, error) { var pk *ecdsa.PrivateKey diff --git a/agent/consul/leader.go b/agent/consul/leader.go index d9c3a83eaf..2f01b88333 100644 --- a/agent/consul/leader.go +++ b/agent/consul/leader.go @@ -426,7 +426,7 @@ func (s *Server) initializeCA() error { return fmt.Errorf("error getting root cert: %v", err) } - id, err := connect.ParseCertFingerprint(rootPEM) + id, err := connect.CalculateCertFingerprint(rootPEM) if err != nil { return fmt.Errorf("error parsing root fingerprint: %v", err) } diff --git a/agent/structs/connect_ca.go b/agent/structs/connect_ca.go index c46db703a8..0570057b61 100644 --- a/agent/structs/connect_ca.go +++ b/agent/structs/connect_ca.go @@ -168,10 +168,10 @@ type ConsulCAProviderConfig struct { // CAConsulProviderState is used to track the built-in Consul CA provider's state. type CAConsulProviderState struct { - ID string - PrivateKey string - RootCert string - LeafIndex uint64 + ID string + PrivateKey string + RootCert string + SerialIndex uint64 RaftIndex }