connect/ca: hash the consul provider ID and include isRoot

This commit is contained in:
Kyle Havlovitz 2018-09-12 13:44:15 -07:00
parent 8fc2c77fdf
commit 70c43a27c3
2 changed files with 68 additions and 5 deletions

View File

@ -3,6 +3,7 @@ package ca
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
@ -10,6 +11,7 @@ import (
"fmt"
"math/big"
"net/url"
"strings"
"sync"
"time"
@ -35,7 +37,7 @@ type ConsulProviderStateDelegate interface {
}
// Configure sets up the provider using the given configuration.
func (c *ConsulProvider) Configure(clusterId string, isRoot bool, rawConfig map[string]interface{}) error {
func (c *ConsulProvider) Configure(clusterID string, isRoot bool, rawConfig map[string]interface{}) error {
// Parse the raw config and update our ID.
config, err := ParseConsulCAConfig(rawConfig)
if err != nil {
@ -43,7 +45,8 @@ func (c *ConsulProvider) Configure(clusterId string, isRoot bool, rawConfig map[
}
c.config = config
c.isRoot = isRoot
c.id = fmt.Sprintf("%s,%s", config.PrivateKey, config.RootCert)
hash := sha256.Sum256([]byte(fmt.Sprintf("%s,%s,%v", config.PrivateKey, config.RootCert, isRoot)))
c.id = strings.Replace(fmt.Sprintf("% x", hash), " ", ":", -1)
// Exit early if the state store has an entry for this provider's config.
_, providerState, err := c.Delegate.State().CAProviderState(c.id)
@ -55,6 +58,37 @@ func (c *ConsulProvider) Configure(clusterId string, isRoot bool, rawConfig map[
return nil
}
// Check if there's an entry with the old ID scheme.
oldID := fmt.Sprintf("%s,%s", config.PrivateKey, config.RootCert)
_, providerState, err = c.Delegate.State().CAProviderState(oldID)
if err != nil {
return err
}
// Found an entry with the old ID, so update it to the new ID and
// delete the old entry.
if providerState != nil {
newState := *providerState
newState.ID = c.id
createReq := &structs.CARequest{
Op: structs.CAOpSetProviderState,
ProviderState: &newState,
}
if err := c.Delegate.ApplyCARequest(createReq); err != nil {
return err
}
deleteReq := &structs.CARequest{
Op: structs.CAOpDeleteProviderState,
ProviderState: providerState,
}
if err := c.Delegate.ApplyCARequest(deleteReq); err != nil {
return err
}
return nil
}
// Write the provider state to the state store.
newState := structs.CAConsulProviderState{
ID: c.id,
@ -363,9 +397,9 @@ func (c *ConsulProvider) generateCA(privateKey string, sn uint64) (string, error
serialNum := &big.Int{}
serialNum.SetUint64(sn)
template := x509.Certificate{
SerialNumber: serialNum,
Subject: pkix.Name{CommonName: name},
URIs: []*url.URL{id.URI()},
SerialNumber: serialNum,
Subject: pkix.Name{CommonName: name},
URIs: []*url.URL{id.URI()},
BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageCertSign |
x509.KeyUsageCRLSign |

View File

@ -271,3 +271,32 @@ func testCrossSignProviders(t *testing.T, provider1, provider2 Provider) {
require.NoError(err)
}
}
func TestConsulCAProvider_MigrateOldID(t *testing.T) {
t.Parallel()
require := require.New(t)
conf := testConsulCAConfig()
delegate := newMockDelegate(t, conf)
// Create an entry with an old-style ID.
err := delegate.ApplyCARequest(&structs.CARequest{
Op: structs.CAOpSetProviderState,
ProviderState: &structs.CAConsulProviderState{
ID: ",",
},
})
require.NoError(err)
_, providerState, err := delegate.state.CAProviderState(",")
require.NoError(err)
require.NotNil(providerState)
provider := &ConsulProvider{Delegate: delegate}
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
require.NoError(provider.GenerateRoot())
// After running Configure, the old ID entry should be gone.
_, providerState, err = delegate.state.CAProviderState(",")
require.NoError(err)
require.Nil(providerState)
}