mirror of https://github.com/status-im/consul.git
connect/ca: add configurable leaf cert TTL
This commit is contained in:
parent
915f930ce3
commit
d6ca015a42
|
@ -544,6 +544,9 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
|
||||||
"token": "Token",
|
"token": "Token",
|
||||||
"root_pki_path": "RootPKIPath",
|
"root_pki_path": "RootPKIPath",
|
||||||
"intermediate_pki_path": "IntermediatePKIPath",
|
"intermediate_pki_path": "IntermediatePKIPath",
|
||||||
|
|
||||||
|
// Common CA config
|
||||||
|
"leaf_cert_ttl": "LeafCertTTL",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2545,7 +2545,8 @@ func TestFullConfig(t *testing.T) {
|
||||||
"connect": {
|
"connect": {
|
||||||
"ca_provider": "consul",
|
"ca_provider": "consul",
|
||||||
"ca_config": {
|
"ca_config": {
|
||||||
"RotationPeriod": "90h"
|
"RotationPeriod": "90h",
|
||||||
|
"LeafCertTTL": "1h"
|
||||||
},
|
},
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"proxy_defaults": {
|
"proxy_defaults": {
|
||||||
|
@ -3006,7 +3007,8 @@ func TestFullConfig(t *testing.T) {
|
||||||
connect {
|
connect {
|
||||||
ca_provider = "consul"
|
ca_provider = "consul"
|
||||||
ca_config {
|
ca_config {
|
||||||
"RotationPeriod" = "90h"
|
rotation_period = "90h"
|
||||||
|
leaf_cert_ttl = "1h"
|
||||||
}
|
}
|
||||||
enabled = true
|
enabled = true
|
||||||
proxy_defaults {
|
proxy_defaults {
|
||||||
|
@ -3610,6 +3612,7 @@ func TestFullConfig(t *testing.T) {
|
||||||
ConnectCAProvider: "consul",
|
ConnectCAProvider: "consul",
|
||||||
ConnectCAConfig: map[string]interface{}{
|
ConnectCAConfig: map[string]interface{}{
|
||||||
"RotationPeriod": "90h",
|
"RotationPeriod": "90h",
|
||||||
|
"LeafCertTTL": "1h",
|
||||||
},
|
},
|
||||||
ConnectProxyAllowManagedRoot: false,
|
ConnectProxyAllowManagedRoot: false,
|
||||||
ConnectProxyAllowManagedAPIRegistration: false,
|
ConnectProxyAllowManagedAPIRegistration: false,
|
||||||
|
|
|
@ -214,8 +214,7 @@ func (c *ConsulProvider) Sign(csr *x509.CertificateRequest) (string, error) {
|
||||||
x509.ExtKeyUsageClientAuth,
|
x509.ExtKeyUsageClientAuth,
|
||||||
x509.ExtKeyUsageServerAuth,
|
x509.ExtKeyUsageServerAuth,
|
||||||
},
|
},
|
||||||
// todo(kyhavlov): add a way to set the cert lifetime here from the CA config
|
NotAfter: effectiveNow.Add(c.config.LeafCertTTL),
|
||||||
NotAfter: effectiveNow.Add(3 * 24 * time.Hour),
|
|
||||||
NotBefore: effectiveNow,
|
NotBefore: effectiveNow,
|
||||||
AuthorityKeyId: keyId,
|
AuthorityKeyId: keyId,
|
||||||
SubjectKeyId: keyId,
|
SubjectKeyId: keyId,
|
||||||
|
|
|
@ -10,10 +10,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseConsulCAConfig(raw map[string]interface{}) (*structs.ConsulCAProviderConfig, error) {
|
func ParseConsulCAConfig(raw map[string]interface{}) (*structs.ConsulCAProviderConfig, error) {
|
||||||
var config structs.ConsulCAProviderConfig
|
config := structs.ConsulCAProviderConfig{
|
||||||
|
CommonCAProviderConfig: defaultCommonConfig(),
|
||||||
|
}
|
||||||
|
|
||||||
decodeConf := &mapstructure.DecoderConfig{
|
decodeConf := &mapstructure.DecoderConfig{
|
||||||
DecodeHook: ParseDurationFunc(),
|
DecodeHook: ParseDurationFunc(),
|
||||||
ErrorUnused: true,
|
|
||||||
Result: &config,
|
Result: &config,
|
||||||
WeaklyTypedInput: true,
|
WeaklyTypedInput: true,
|
||||||
}
|
}
|
||||||
|
@ -75,3 +77,9 @@ func Uint8ToString(bs []uint8) string {
|
||||||
}
|
}
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func defaultCommonConfig() structs.CommonCAProviderConfig {
|
||||||
|
return structs.CommonCAProviderConfig{
|
||||||
|
LeafCertTTL: 3 * 24 * time.Hour,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -117,12 +117,13 @@ func TestConsulCAProvider_Bootstrap_WithCert(t *testing.T) {
|
||||||
func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := assert.New(t)
|
require := require.New(t)
|
||||||
conf := testConsulCAConfig()
|
conf := testConsulCAConfig()
|
||||||
|
conf.Config["LeafCertTTL"] = "1h"
|
||||||
delegate := newMockDelegate(t, conf)
|
delegate := newMockDelegate(t, conf)
|
||||||
|
|
||||||
provider, err := NewConsulProvider(conf.Config, delegate)
|
provider, err := NewConsulProvider(conf.Config, delegate)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
spiffeService := &connect.SpiffeIDService{
|
spiffeService := &connect.SpiffeIDService{
|
||||||
Host: "node1",
|
Host: "node1",
|
||||||
|
@ -136,20 +137,21 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
||||||
raw, _ := connect.TestCSR(t, spiffeService)
|
raw, _ := connect.TestCSR(t, spiffeService)
|
||||||
|
|
||||||
csr, err := connect.ParseCSR(raw)
|
csr, err := connect.ParseCSR(raw)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
cert, err := provider.Sign(csr)
|
cert, err := provider.Sign(csr)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
parsed, err := connect.ParseCert(cert)
|
parsed, err := connect.ParseCert(cert)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal(parsed.URIs[0], spiffeService.URI())
|
require.Equal(parsed.URIs[0], spiffeService.URI())
|
||||||
assert.Equal(parsed.Subject.CommonName, "foo")
|
require.Equal(parsed.Subject.CommonName, "foo")
|
||||||
assert.Equal(uint64(2), parsed.SerialNumber.Uint64())
|
require.Equal(uint64(2), parsed.SerialNumber.Uint64())
|
||||||
|
|
||||||
// Ensure the cert is valid now and expires within the correct limit.
|
// Ensure the cert is valid now and expires within the correct limit.
|
||||||
assert.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
now := time.Now()
|
||||||
assert.True(parsed.NotBefore.Before(time.Now()))
|
require.True(parsed.NotAfter.Sub(now) < time.Hour)
|
||||||
|
require.True(parsed.NotBefore.Before(now))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new cert for another service and make sure
|
// Generate a new cert for another service and make sure
|
||||||
|
@ -159,20 +161,20 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
||||||
raw, _ := connect.TestCSR(t, spiffeService)
|
raw, _ := connect.TestCSR(t, spiffeService)
|
||||||
|
|
||||||
csr, err := connect.ParseCSR(raw)
|
csr, err := connect.ParseCSR(raw)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
cert, err := provider.Sign(csr)
|
cert, err := provider.Sign(csr)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
parsed, err := connect.ParseCert(cert)
|
parsed, err := connect.ParseCert(cert)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal(parsed.URIs[0], spiffeService.URI())
|
require.Equal(parsed.URIs[0], spiffeService.URI())
|
||||||
assert.Equal(parsed.Subject.CommonName, "bar")
|
require.Equal(parsed.Subject.CommonName, "bar")
|
||||||
assert.Equal(parsed.SerialNumber.Uint64(), uint64(2))
|
require.Equal(parsed.SerialNumber.Uint64(), uint64(2))
|
||||||
|
|
||||||
// Ensure the cert is valid now and expires within the correct limit.
|
// Ensure the cert is valid now and expires within the correct limit.
|
||||||
assert.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
require.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
||||||
assert.True(parsed.NotBefore.Before(time.Now()))
|
require.True(parsed.NotBefore.Before(time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -227,6 +227,7 @@ func (v *VaultProvider) Sign(csr *x509.CertificateRequest) (string, error) {
|
||||||
// Use the leaf cert role to sign a new cert for this CSR.
|
// Use the leaf cert role to sign a new cert for this CSR.
|
||||||
response, err := v.client.Logical().Write(v.config.IntermediatePKIPath+"sign/"+VaultCALeafCertRole, map[string]interface{}{
|
response, err := v.client.Logical().Write(v.config.IntermediatePKIPath+"sign/"+VaultCALeafCertRole, map[string]interface{}{
|
||||||
"csr": pemBuf.String(),
|
"csr": pemBuf.String(),
|
||||||
|
"ttl": fmt.Sprintf("%.0fh", v.config.LeafCertTTL.Hours()),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error issuing cert: %v", err)
|
return "", fmt.Errorf("error issuing cert: %v", err)
|
||||||
|
@ -283,10 +284,12 @@ func (v *VaultProvider) Cleanup() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseVaultCAConfig(raw map[string]interface{}) (*structs.VaultCAProviderConfig, error) {
|
func ParseVaultCAConfig(raw map[string]interface{}) (*structs.VaultCAProviderConfig, error) {
|
||||||
var config structs.VaultCAProviderConfig
|
config := structs.VaultCAProviderConfig{
|
||||||
|
CommonCAProviderConfig: defaultCommonConfig(),
|
||||||
|
}
|
||||||
|
|
||||||
decodeConf := &mapstructure.DecoderConfig{
|
decodeConf := &mapstructure.DecoderConfig{
|
||||||
ErrorUnused: true,
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
||||||
Result: &config,
|
Result: &config,
|
||||||
WeaklyTypedInput: true,
|
WeaklyTypedInput: true,
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func testVaultCluster(t *testing.T) (*VaultProvider, *vault.Core, net.Listener) {
|
func testVaultCluster(t *testing.T) (*VaultProvider, *vault.Core, net.Listener) {
|
||||||
|
return testVaultClusterWithConfig(t, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVaultClusterWithConfig(t *testing.T, rawConf map[string]interface{}) (*VaultProvider, *vault.Core, net.Listener) {
|
||||||
if err := vault.AddTestLogicalBackend("pki", pki.Factory); err != nil {
|
if err := vault.AddTestLogicalBackend("pki", pki.Factory); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -23,12 +27,17 @@ func testVaultCluster(t *testing.T) (*VaultProvider, *vault.Core, net.Listener)
|
||||||
|
|
||||||
ln, addr := vaulthttp.TestServer(t, core)
|
ln, addr := vaulthttp.TestServer(t, core)
|
||||||
|
|
||||||
provider, err := NewVaultProvider(map[string]interface{}{
|
conf := map[string]interface{}{
|
||||||
"Address": addr,
|
"Address": addr,
|
||||||
"Token": token,
|
"Token": token,
|
||||||
"RootPKIPath": "pki-root/",
|
"RootPKIPath": "pki-root/",
|
||||||
"IntermediatePKIPath": "pki-intermediate/",
|
"IntermediatePKIPath": "pki-intermediate/",
|
||||||
}, "asdf")
|
}
|
||||||
|
for k, v := range rawConf {
|
||||||
|
conf[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := NewVaultProvider(conf, "asdf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -87,7 +96,9 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
provider, core, listener := testVaultCluster(t)
|
provider, core, listener := testVaultClusterWithConfig(t, map[string]interface{}{
|
||||||
|
"LeafCertTTL": "1h",
|
||||||
|
})
|
||||||
defer core.Shutdown()
|
defer core.Shutdown()
|
||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
client, err := vaultapi.NewClient(&vaultapi.Config{
|
client, err := vaultapi.NewClient(&vaultapi.Config{
|
||||||
|
@ -120,8 +131,9 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) {
|
||||||
firstSerial = parsed.SerialNumber.Uint64()
|
firstSerial = parsed.SerialNumber.Uint64()
|
||||||
|
|
||||||
// Ensure the cert is valid now and expires within the correct limit.
|
// Ensure the cert is valid now and expires within the correct limit.
|
||||||
require.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
now := time.Now()
|
||||||
require.True(parsed.NotBefore.Before(time.Now()))
|
require.True(parsed.NotAfter.Sub(now) < time.Hour)
|
||||||
|
require.True(parsed.NotBefore.Before(now))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new cert for another service and make sure
|
// Generate a new cert for another service and make sure
|
||||||
|
|
|
@ -68,6 +68,7 @@ func TestConnectCAConfig(t *testing.T) {
|
||||||
expected := &structs.ConsulCAProviderConfig{
|
expected := &structs.ConsulCAProviderConfig{
|
||||||
RotationPeriod: 90 * 24 * time.Hour,
|
RotationPeriod: 90 * 24 * time.Hour,
|
||||||
}
|
}
|
||||||
|
expected.LeafCertTTL = 72 * time.Hour
|
||||||
|
|
||||||
// Get the initial config.
|
// Get the initial config.
|
||||||
{
|
{
|
||||||
|
@ -89,7 +90,8 @@ func TestConnectCAConfig(t *testing.T) {
|
||||||
{
|
{
|
||||||
"Provider": "consul",
|
"Provider": "consul",
|
||||||
"Config": {
|
"Config": {
|
||||||
"RotationPeriod": 3600000000000
|
"LeafCertTTL": "72h",
|
||||||
|
"RotationPeriod": "1h"
|
||||||
}
|
}
|
||||||
}`))
|
}`))
|
||||||
req, _ := http.NewRequest("PUT", "/v1/connect/ca/configuration", body)
|
req, _ := http.NewRequest("PUT", "/v1/connect/ca/configuration", body)
|
||||||
|
|
|
@ -438,6 +438,7 @@ func DefaultConfig() *Config {
|
||||||
Provider: "consul",
|
Provider: "consul",
|
||||||
Config: map[string]interface{}{
|
Config: map[string]interface{}{
|
||||||
"RotationPeriod": "2160h",
|
"RotationPeriod": "2160h",
|
||||||
|
"LeafCertTTL": "72h",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -192,7 +192,13 @@ type CAConfiguration struct {
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommonCAProviderConfig struct {
|
||||||
|
LeafCertTTL time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
type ConsulCAProviderConfig struct {
|
type ConsulCAProviderConfig struct {
|
||||||
|
CommonCAProviderConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
RootCert string
|
RootCert string
|
||||||
RotationPeriod time.Duration
|
RotationPeriod time.Duration
|
||||||
|
@ -208,6 +214,8 @@ type CAConsulProviderState struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type VaultCAProviderConfig struct {
|
type VaultCAProviderConfig struct {
|
||||||
|
CommonCAProviderConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
Address string
|
Address string
|
||||||
Token string
|
Token string
|
||||||
RootPKIPath string
|
RootPKIPath string
|
||||||
|
|
|
@ -21,8 +21,15 @@ type CAConfig struct {
|
||||||
ModifyIndex uint64
|
ModifyIndex uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommonCAProviderConfig is the common options available to all CA providers.
|
||||||
|
type CommonCAProviderConfig struct {
|
||||||
|
LeafCertTTL time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// ConsulCAProviderConfig is the config for the built-in Consul CA provider.
|
// ConsulCAProviderConfig is the config for the built-in Consul CA provider.
|
||||||
type ConsulCAProviderConfig struct {
|
type ConsulCAProviderConfig struct {
|
||||||
|
CommonCAProviderConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
RootCert string
|
RootCert string
|
||||||
RotationPeriod time.Duration
|
RotationPeriod time.Duration
|
||||||
|
|
|
@ -64,6 +64,7 @@ func TestAPI_ConnectCAConfig_get_set(t *testing.T) {
|
||||||
expected := &ConsulCAProviderConfig{
|
expected := &ConsulCAProviderConfig{
|
||||||
RotationPeriod: 90 * 24 * time.Hour,
|
RotationPeriod: 90 * 24 * time.Hour,
|
||||||
}
|
}
|
||||||
|
expected.LeafCertTTL = 72 * time.Hour
|
||||||
|
|
||||||
// This fails occasionally if server doesn't have time to bootstrap CA so
|
// This fails occasionally if server doesn't have time to bootstrap CA so
|
||||||
// retry
|
// retry
|
||||||
|
|
Loading…
Reference in New Issue