Skip to content

Commit

Permalink
Move generation of the CA Configuration from the agent code into a me…
Browse files Browse the repository at this point in the history
…thod on the RuntimeConfig

This allows this to be reused elsewhere.
  • Loading branch information
mkeeler committed Jul 23, 2020
1 parent 2713c0e commit 0550445
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 33 deletions.
34 changes: 4 additions & 30 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -1460,38 +1460,12 @@ func (a *Agent) consulConfig() (*consul.Config, error) {
base.ConnectEnabled = true
base.ConnectMeshGatewayWANFederationEnabled = a.config.ConnectMeshGatewayWANFederationEnabled

// Allow config to specify cluster_id provided it's a valid UUID. This is
// meant only for tests where a deterministic ID makes fixtures much simpler
// to work with but since it's only read on initial cluster bootstrap it's not
// that much of a liability in production. The worst a user could do is
// configure logically separate clusters with same ID by mistake but we can
// avoid documenting this is even an option.
if clusterID, ok := a.config.ConnectCAConfig["cluster_id"]; ok {
if cIDStr, ok := clusterID.(string); ok {
if _, err := uuid.ParseUUID(cIDStr); err == nil {
// Valid UUID configured, use that
base.CAConfig.ClusterID = cIDStr
}
}
if base.CAConfig.ClusterID == "" {
// If the tried to specify an ID but typoed it don't ignore as they will
// then bootstrap with a new ID and have to throw away the whole cluster
// and start again.
a.logger.Error("connect CA config cluster_id specified but " +
"is not a valid UUID, aborting startup")
return nil, fmt.Errorf("cluster_id was supplied but was not a valid UUID")
}
}

if a.config.ConnectCAProvider != "" {
base.CAConfig.Provider = a.config.ConnectCAProvider
ca, err := a.config.ConnectCAConfiguration()
if err != nil {
return nil, err
}

// Merge connect CA Config regardless of provider (since there are some
// common config options valid to all like leaf TTL).
for k, v := range a.config.ConnectCAConfig {
base.CAConfig.Config[k] = v
}
base.CAConfig = ca
}

// copy over auto config settings
Expand Down
55 changes: 55 additions & 0 deletions agent/config/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/hashicorp/consul/lib"
"github.com/hashicorp/consul/tlsutil"
"github.com/hashicorp/consul/types"
"github.com/hashicorp/go-uuid"
"golang.org/x/time/rate"
)

Expand Down Expand Up @@ -1686,6 +1687,60 @@ func (c *RuntimeConfig) ClientAddress() (unixAddr, httpAddr, httpsAddr string) {
return
}

func (c *RuntimeConfig) ConnectCAConfiguration() (*structs.CAConfiguration, error) {
if !c.ConnectEnabled {
return nil, nil
}

ca := &structs.CAConfiguration{
Provider: "consul",
Config: map[string]interface{}{
"RotationPeriod": structs.DefaultCARotationPeriod,
"LeafCertTTL": structs.DefaultLeafCertTTL,
"IntermediateCertTTL": structs.DefaultIntermediateCertTTL,
},
}

// Allow config to specify cluster_id provided it's a valid UUID. This is
// meant only for tests where a deterministic ID makes fixtures much simpler
// to work with but since it's only read on initial cluster bootstrap it's not
// that much of a liability in production. The worst a user could do is
// configure logically separate clusters with same ID by mistake but we can
// avoid documenting this is even an option.
if clusterID, ok := c.ConnectCAConfig["cluster_id"]; ok {
// If they tried to specify an ID but typoed it then don't ignore as they
// will then bootstrap with a new ID and have to throw away the whole cluster
// and start again.

// ensure the cluster_id value in the opaque config is a string
cIDStr, ok := clusterID.(string)
if !ok {
return nil, fmt.Errorf("cluster_id was supplied but was not a string")
}

// ensure that the cluster_id string is a valid UUID
_, err := uuid.ParseUUID(cIDStr)
if err != nil {
return nil, fmt.Errorf("cluster_id was supplied but was not a valid UUID")
}

// now that we know the cluster_id is okay we can set it in the CAConfiguration
ca.ClusterID = cIDStr
}

if c.ConnectCAProvider != "" {
ca.Provider = c.ConnectCAProvider
}

// Merge connect CA Config regardless of provider (since there are some
// common config options valid to all like leaf TTL).
for k, v := range c.ConnectCAConfig {
ca.Config[k] = v
}

return ca, nil
}

func (c *RuntimeConfig) APIConfig(includeClientCerts bool) (*api.Config, error) {
cfg := &api.Config{
Datacenter: c.Datacenter,
Expand Down
100 changes: 100 additions & 0 deletions agent/config/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7440,3 +7440,103 @@ func metaPairs(n int, format string) string {
panic("invalid format: " + format)
}
}

func TestConnectCAConfiguration(t *testing.T) {
type testCase struct {
config RuntimeConfig
expected *structs.CAConfiguration
err string
}

cases := map[string]testCase{
"connect-disabled": {
config: RuntimeConfig{
ConnectEnabled: false,
},
expected: nil,
},
"defaults": {
config: RuntimeConfig{
ConnectEnabled: true,
},
expected: &structs.CAConfiguration{
Provider: "consul",
Config: map[string]interface{}{
"RotationPeriod": "2160h",
"LeafCertTTL": "72h",
"IntermediateCertTTL": "8760h", // 365 * 24h
},
},
},
"cluster-id-override": {
config: RuntimeConfig{
ConnectEnabled: true,
ConnectCAConfig: map[string]interface{}{
"cluster_id": "adfe7697-09b4-413a-ac0a-fa81ed3a3001",
},
},
expected: &structs.CAConfiguration{
Provider: "consul",
ClusterID: "adfe7697-09b4-413a-ac0a-fa81ed3a3001",
Config: map[string]interface{}{
"RotationPeriod": "2160h",
"LeafCertTTL": "72h",
"IntermediateCertTTL": "8760h", // 365 * 24h
"cluster_id": "adfe7697-09b4-413a-ac0a-fa81ed3a3001",
},
},
},
"cluster-id-non-uuid": {
config: RuntimeConfig{
ConnectEnabled: true,
ConnectCAConfig: map[string]interface{}{
"cluster_id": "foo",
},
},
err: "cluster_id was supplied but was not a valid UUID",
},
"provider-override": {
config: RuntimeConfig{
ConnectEnabled: true,
ConnectCAProvider: "vault",
},
expected: &structs.CAConfiguration{
Provider: "vault",
Config: map[string]interface{}{
"RotationPeriod": "2160h",
"LeafCertTTL": "72h",
"IntermediateCertTTL": "8760h", // 365 * 24h
},
},
},
"other-config": {
config: RuntimeConfig{
ConnectEnabled: true,
ConnectCAConfig: map[string]interface{}{
"foo": "bar",
},
},
expected: &structs.CAConfiguration{
Provider: "consul",
Config: map[string]interface{}{
"RotationPeriod": "2160h",
"LeafCertTTL": "72h",
"IntermediateCertTTL": "8760h", // 365 * 24h
"foo": "bar",
},
},
},
}

for name, tcase := range cases {
t.Run(name, func(t *testing.T) {
actual, err := tcase.config.ConnectCAConfiguration()
if tcase.err != "" {
testutil.RequireErrorContains(t, err, tcase.err)
} else {
require.NoError(t, err)
require.Equal(t, tcase.expected, actual)
}
})
}
}
6 changes: 3 additions & 3 deletions agent/consul/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,9 +594,9 @@ func DefaultConfig() *Config {
CAConfig: &structs.CAConfiguration{
Provider: "consul",
Config: map[string]interface{}{
"RotationPeriod": "2160h",
"LeafCertTTL": "72h",
"IntermediateCertTTL": "8760h", // 365 * 24h
"RotationPeriod": structs.DefaultCARotationPeriod,
"LeafCertTTL": structs.DefaultLeafCertTTL,
"IntermediateCertTTL": structs.DefaultIntermediateCertTTL,
},
},

Expand Down
6 changes: 6 additions & 0 deletions agent/structs/connect_ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import (
"github.com/mitchellh/mapstructure"
)

const (
DefaultCARotationPeriod = "2160h"
DefaultLeafCertTTL = "72h"
DefaultIntermediateCertTTL = "8760h" // 365 * 24h
)

// IndexedCARoots is the list of currently trusted CA Roots.
type IndexedCARoots struct {
// ActiveRootID is the ID of a root in Roots that is the active CA root.
Expand Down

0 comments on commit 0550445

Please sign in to comment.