Merge pull request #4400 from hashicorp/leaf-cert-ttl
Add configurable leaf cert TTL to Connect CA
This commit is contained in:
commit
ecc02c6aee
|
@ -544,6 +544,9 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
|
|||
"token": "Token",
|
||||
"root_pki_path": "RootPKIPath",
|
||||
"intermediate_pki_path": "IntermediatePKIPath",
|
||||
|
||||
// Common CA config
|
||||
"leaf_cert_ttl": "LeafCertTTL",
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -2602,7 +2602,8 @@ func TestFullConfig(t *testing.T) {
|
|||
"connect": {
|
||||
"ca_provider": "consul",
|
||||
"ca_config": {
|
||||
"RotationPeriod": "90h"
|
||||
"RotationPeriod": "90h",
|
||||
"LeafCertTTL": "1h"
|
||||
},
|
||||
"enabled": true,
|
||||
"proxy_defaults": {
|
||||
|
@ -3073,7 +3074,8 @@ func TestFullConfig(t *testing.T) {
|
|||
connect {
|
||||
ca_provider = "consul"
|
||||
ca_config {
|
||||
"RotationPeriod" = "90h"
|
||||
rotation_period = "90h"
|
||||
leaf_cert_ttl = "1h"
|
||||
}
|
||||
enabled = true
|
||||
proxy_defaults {
|
||||
|
@ -3687,6 +3689,7 @@ func TestFullConfig(t *testing.T) {
|
|||
ConnectCAProvider: "consul",
|
||||
ConnectCAConfig: map[string]interface{}{
|
||||
"RotationPeriod": "90h",
|
||||
"LeafCertTTL": "1h",
|
||||
},
|
||||
ConnectProxyAllowManagedRoot: false,
|
||||
ConnectProxyAllowManagedAPIRegistration: false,
|
||||
|
|
|
@ -214,8 +214,7 @@ func (c *ConsulProvider) 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: effectiveNow.Add(3 * 24 * time.Hour),
|
||||
NotAfter: effectiveNow.Add(c.config.LeafCertTTL),
|
||||
NotBefore: effectiveNow,
|
||||
AuthorityKeyId: keyId,
|
||||
SubjectKeyId: keyId,
|
||||
|
|
|
@ -10,10 +10,12 @@ import (
|
|||
)
|
||||
|
||||
func ParseConsulCAConfig(raw map[string]interface{}) (*structs.ConsulCAProviderConfig, error) {
|
||||
var config structs.ConsulCAProviderConfig
|
||||
config := structs.ConsulCAProviderConfig{
|
||||
CommonCAProviderConfig: defaultCommonConfig(),
|
||||
}
|
||||
|
||||
decodeConf := &mapstructure.DecoderConfig{
|
||||
DecodeHook: ParseDurationFunc(),
|
||||
ErrorUnused: true,
|
||||
Result: &config,
|
||||
WeaklyTypedInput: true,
|
||||
}
|
||||
|
@ -31,6 +33,10 @@ func ParseConsulCAConfig(raw map[string]interface{}) (*structs.ConsulCAProviderC
|
|||
return nil, fmt.Errorf("must provide a private key when providing a root cert")
|
||||
}
|
||||
|
||||
if err := config.CommonCAProviderConfig.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
|
@ -75,3 +81,9 @@ func Uint8ToString(bs []uint8) string {
|
|||
}
|
||||
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) {
|
||||
t.Parallel()
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
conf := testConsulCAConfig()
|
||||
conf.Config["LeafCertTTL"] = "1h"
|
||||
delegate := newMockDelegate(t, conf)
|
||||
|
||||
provider, err := NewConsulProvider(conf.Config, delegate)
|
||||
assert.NoError(err)
|
||||
require.NoError(err)
|
||||
|
||||
spiffeService := &connect.SpiffeIDService{
|
||||
Host: "node1",
|
||||
|
@ -136,20 +137,21 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
|||
raw, _ := connect.TestCSR(t, spiffeService)
|
||||
|
||||
csr, err := connect.ParseCSR(raw)
|
||||
assert.NoError(err)
|
||||
require.NoError(err)
|
||||
|
||||
cert, err := provider.Sign(csr)
|
||||
assert.NoError(err)
|
||||
require.NoError(err)
|
||||
|
||||
parsed, err := connect.ParseCert(cert)
|
||||
assert.NoError(err)
|
||||
assert.Equal(parsed.URIs[0], spiffeService.URI())
|
||||
assert.Equal(parsed.Subject.CommonName, "foo")
|
||||
assert.Equal(uint64(2), parsed.SerialNumber.Uint64())
|
||||
require.NoError(err)
|
||||
require.Equal(parsed.URIs[0], spiffeService.URI())
|
||||
require.Equal(parsed.Subject.CommonName, "foo")
|
||||
require.Equal(uint64(2), parsed.SerialNumber.Uint64())
|
||||
|
||||
// Ensure the cert is valid now and expires within the correct limit.
|
||||
assert.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
||||
assert.True(parsed.NotBefore.Before(time.Now()))
|
||||
now := 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
|
||||
|
@ -159,20 +161,20 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
|||
raw, _ := connect.TestCSR(t, spiffeService)
|
||||
|
||||
csr, err := connect.ParseCSR(raw)
|
||||
assert.NoError(err)
|
||||
require.NoError(err)
|
||||
|
||||
cert, err := provider.Sign(csr)
|
||||
assert.NoError(err)
|
||||
require.NoError(err)
|
||||
|
||||
parsed, err := connect.ParseCert(cert)
|
||||
assert.NoError(err)
|
||||
assert.Equal(parsed.URIs[0], spiffeService.URI())
|
||||
assert.Equal(parsed.Subject.CommonName, "bar")
|
||||
assert.Equal(parsed.SerialNumber.Uint64(), uint64(2))
|
||||
require.NoError(err)
|
||||
require.Equal(parsed.URIs[0], spiffeService.URI())
|
||||
require.Equal(parsed.Subject.CommonName, "bar")
|
||||
require.Equal(parsed.SerialNumber.Uint64(), uint64(2))
|
||||
|
||||
// Ensure the cert is valid now and expires within the correct limit.
|
||||
assert.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
||||
assert.True(parsed.NotBefore.Before(time.Now()))
|
||||
require.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
||||
require.True(parsed.NotBefore.Before(time.Now()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -172,7 +172,7 @@ func (v *VaultProvider) GenerateIntermediate() (string, error) {
|
|||
"allow_any_name": true,
|
||||
"allowed_uri_sans": "spiffe://*",
|
||||
"key_type": "any",
|
||||
"max_ttl": "72h",
|
||||
"max_ttl": v.config.LeafCertTTL.String(),
|
||||
"require_cn": false,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -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.
|
||||
response, err := v.client.Logical().Write(v.config.IntermediatePKIPath+"sign/"+VaultCALeafCertRole, map[string]interface{}{
|
||||
"csr": pemBuf.String(),
|
||||
"ttl": v.config.LeafCertTTL.String(),
|
||||
})
|
||||
if err != nil {
|
||||
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) {
|
||||
var config structs.VaultCAProviderConfig
|
||||
config := structs.VaultCAProviderConfig{
|
||||
CommonCAProviderConfig: defaultCommonConfig(),
|
||||
}
|
||||
|
||||
decodeConf := &mapstructure.DecoderConfig{
|
||||
ErrorUnused: true,
|
||||
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
||||
Result: &config,
|
||||
WeaklyTypedInput: true,
|
||||
}
|
||||
|
@ -318,5 +321,9 @@ func ParseVaultCAConfig(raw map[string]interface{}) (*structs.VaultCAProviderCon
|
|||
config.IntermediatePKIPath += "/"
|
||||
}
|
||||
|
||||
if err := config.CommonCAProviderConfig.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ import (
|
|||
)
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -23,12 +27,17 @@ func testVaultCluster(t *testing.T) (*VaultProvider, *vault.Core, net.Listener)
|
|||
|
||||
ln, addr := vaulthttp.TestServer(t, core)
|
||||
|
||||
provider, err := NewVaultProvider(map[string]interface{}{
|
||||
conf := map[string]interface{}{
|
||||
"Address": addr,
|
||||
"Token": token,
|
||||
"RootPKIPath": "pki-root/",
|
||||
"IntermediatePKIPath": "pki-intermediate/",
|
||||
}, "asdf")
|
||||
}
|
||||
for k, v := range rawConf {
|
||||
conf[k] = v
|
||||
}
|
||||
|
||||
provider, err := NewVaultProvider(conf, "asdf")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -87,7 +96,9 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
provider, core, listener := testVaultCluster(t)
|
||||
provider, core, listener := testVaultClusterWithConfig(t, map[string]interface{}{
|
||||
"LeafCertTTL": "1h",
|
||||
})
|
||||
defer core.Shutdown()
|
||||
defer listener.Close()
|
||||
client, err := vaultapi.NewClient(&vaultapi.Config{
|
||||
|
@ -120,8 +131,9 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) {
|
|||
firstSerial = parsed.SerialNumber.Uint64()
|
||||
|
||||
// Ensure the cert is valid now and expires within the correct limit.
|
||||
require.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
||||
require.True(parsed.NotBefore.Before(time.Now()))
|
||||
now := 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
|
||||
|
@ -142,7 +154,7 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) {
|
|||
require.NotEqual(firstSerial, parsed.SerialNumber.Uint64())
|
||||
|
||||
// Ensure the cert is valid now and expires within the correct limit.
|
||||
require.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
|
||||
require.True(parsed.NotAfter.Sub(time.Now()) < time.Hour)
|
||||
require.True(parsed.NotBefore.Before(time.Now()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ func TestConnectCAConfig(t *testing.T) {
|
|||
expected := &structs.ConsulCAProviderConfig{
|
||||
RotationPeriod: 90 * 24 * time.Hour,
|
||||
}
|
||||
expected.LeafCertTTL = 72 * time.Hour
|
||||
|
||||
// Get the initial config.
|
||||
{
|
||||
|
@ -88,7 +89,8 @@ func TestConnectCAConfig(t *testing.T) {
|
|||
{
|
||||
"Provider": "consul",
|
||||
"Config": {
|
||||
"RotationPeriod": 3600000000000
|
||||
"LeafCertTTL": "72h",
|
||||
"RotationPeriod": "1h"
|
||||
}
|
||||
}`))
|
||||
req, _ := http.NewRequest("PUT", "/v1/connect/ca/configuration", body)
|
||||
|
|
|
@ -438,6 +438,7 @@ func DefaultConfig() *Config {
|
|||
Provider: "consul",
|
||||
Config: map[string]interface{}{
|
||||
"RotationPeriod": "2160h",
|
||||
"LeafCertTTL": "72h",
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -32,10 +32,6 @@ var (
|
|||
// caRootPruneInterval is how often we check for stale CARoots to remove.
|
||||
caRootPruneInterval = time.Hour
|
||||
|
||||
// caRootExpireDuration is the duration after which an inactive root is considered
|
||||
// "expired". Currently this is based on the default leaf cert TTL of 3 days.
|
||||
caRootExpireDuration = 7 * 24 * time.Hour
|
||||
|
||||
// minAutopilotVersion is the minimum Consul version in which Autopilot features
|
||||
// are supported.
|
||||
minAutopilotVersion = version.Must(version.NewVersion("0.8.0"))
|
||||
|
@ -601,14 +597,25 @@ func (s *Server) pruneCARoots() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
idx, roots, err := s.fsm.State().CARoots(nil)
|
||||
state := s.fsm.State()
|
||||
idx, roots, err := state.CARoots(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, caConf, err := state.CAConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
common, err := caConf.GetCommonConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var newRoots structs.CARoots
|
||||
for _, r := range roots {
|
||||
if !r.Active && !r.RotatedOutAt.IsZero() && time.Now().Sub(r.RotatedOutAt) > caRootExpireDuration {
|
||||
if !r.Active && !r.RotatedOutAt.IsZero() && time.Now().Sub(r.RotatedOutAt) > common.LeafCertTTL*2 {
|
||||
s.logger.Printf("[INFO] connect: pruning old unused root CA (ID: %s)", r.ID)
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -1008,7 +1008,6 @@ func TestLeader_ACL_Initialization(t *testing.T) {
|
|||
func TestLeader_CARootPruning(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
caRootExpireDuration = 500 * time.Millisecond
|
||||
caRootPruneInterval = 200 * time.Millisecond
|
||||
|
||||
require := require.New(t)
|
||||
|
@ -1036,9 +1035,11 @@ func TestLeader_CARootPruning(t *testing.T) {
|
|||
newConfig := &structs.CAConfiguration{
|
||||
Provider: "consul",
|
||||
Config: map[string]interface{}{
|
||||
"LeafCertTTL": 500 * time.Millisecond,
|
||||
"PrivateKey": newKey,
|
||||
"RootCert": "",
|
||||
"RotationPeriod": 90 * 24 * time.Hour,
|
||||
"SkipValidate": true,
|
||||
},
|
||||
}
|
||||
{
|
||||
|
@ -1056,7 +1057,7 @@ func TestLeader_CARootPruning(t *testing.T) {
|
|||
require.NoError(err)
|
||||
require.Len(roots, 2)
|
||||
|
||||
time.Sleep(caRootExpireDuration * 2)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Now the old root should be pruned.
|
||||
_, roots, err = s1.fsm.State().CARoots(nil)
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// IndexedCARoots is the list of currently trusted CA Roots.
|
||||
|
@ -192,7 +195,54 @@ type CAConfiguration struct {
|
|||
RaftIndex
|
||||
}
|
||||
|
||||
func (c *CAConfiguration) GetCommonConfig() (*CommonCAProviderConfig, error) {
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("config map was nil")
|
||||
}
|
||||
|
||||
var config CommonCAProviderConfig
|
||||
decodeConf := &mapstructure.DecoderConfig{
|
||||
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
||||
Result: &config,
|
||||
}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(decodeConf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := decoder.Decode(c.Config); err != nil {
|
||||
return nil, fmt.Errorf("error decoding config: %s", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
type CommonCAProviderConfig struct {
|
||||
LeafCertTTL time.Duration
|
||||
|
||||
SkipValidate bool
|
||||
}
|
||||
|
||||
func (c CommonCAProviderConfig) Validate() error {
|
||||
if c.SkipValidate {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.LeafCertTTL < time.Hour {
|
||||
return fmt.Errorf("leaf cert TTL must be greater than 1h")
|
||||
}
|
||||
|
||||
if c.LeafCertTTL > 365*24*time.Hour {
|
||||
return fmt.Errorf("leaf cert TTL must be less than 1 year")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ConsulCAProviderConfig struct {
|
||||
CommonCAProviderConfig `mapstructure:",squash"`
|
||||
|
||||
PrivateKey string
|
||||
RootCert string
|
||||
RotationPeriod time.Duration
|
||||
|
@ -208,6 +258,8 @@ type CAConsulProviderState struct {
|
|||
}
|
||||
|
||||
type VaultCAProviderConfig struct {
|
||||
CommonCAProviderConfig `mapstructure:",squash"`
|
||||
|
||||
Address string
|
||||
Token string
|
||||
RootPKIPath string
|
||||
|
|
|
@ -21,8 +21,15 @@ type CAConfig struct {
|
|||
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.
|
||||
type ConsulCAProviderConfig struct {
|
||||
CommonCAProviderConfig `mapstructure:",squash"`
|
||||
|
||||
PrivateKey string
|
||||
RootCert string
|
||||
RotationPeriod time.Duration
|
||||
|
|
|
@ -63,6 +63,7 @@ func TestAPI_ConnectCAConfig_get_set(t *testing.T) {
|
|||
expected := &ConsulCAProviderConfig{
|
||||
RotationPeriod: 90 * 24 * time.Hour,
|
||||
}
|
||||
expected.LeafCertTTL = 72 * time.Hour
|
||||
|
||||
// This fails occasionally if server doesn't have time to bootstrap CA so
|
||||
// retry
|
||||
|
|
|
@ -91,8 +91,7 @@ $ curl \
|
|||
{
|
||||
"Provider": "consul",
|
||||
"Config": {
|
||||
"PrivateKey": null,
|
||||
"RootCert": null,
|
||||
"LeafCertTTL": "72h",
|
||||
"RotationPeriod": "2160h"
|
||||
},
|
||||
"CreateIndex": 5,
|
||||
|
@ -133,8 +132,10 @@ providers, see [Provider Config](/docs/connect/ca.html).
|
|||
{
|
||||
"Provider": "consul",
|
||||
"Config": {
|
||||
"LeafCertTTL": "72h",
|
||||
"PrivateKey": "-----BEGIN RSA PRIVATE KEY-----...",
|
||||
"RootCert": "-----BEGIN CERTIFICATE-----...",
|
||||
"RotationPeriod": "2160h"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -728,6 +728,21 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
|
|||
`write` access to this backend, as well as permission to mount the backend at this path if it is not
|
||||
already mounted.
|
||||
|
||||
#### Common CA Config Options
|
||||
|
||||
<p>There are also a number of common configuration options supported by all providers:</p>
|
||||
|
||||
* <a name="ca_leaf_cert_ttl"></a><a href="#ca_leaf_cert_ttl">`leaf_cert_ttl`</a> The upper bound on the
|
||||
lease duration of a leaf certificate issued for a service. In most cases a new leaf certificate will be
|
||||
requested by a proxy before this limit is reached. This is also the effective limit on how long a server
|
||||
outage can last (with no leader) before network connections will start being rejected, and as a result the
|
||||
defaults is `72h` to last through a weekend without intervention. This value cannot be lower than 1 hour
|
||||
or higher than 1 year.
|
||||
|
||||
This value is also used when rotating out old root certificates from the cluster. When a root certificate
|
||||
has been inactive (rotated out) for more than twice the *current* `leaf_cert_ttl`, it will be removed from
|
||||
the trusted list.
|
||||
|
||||
* <a name="connect_proxy"></a><a href="#connect_proxy">`proxy`</a> This object allows setting options for the Connect proxies. The following sub-keys are available:
|
||||
|
||||
* <a name="connect_proxy_allow_managed_registration"></a><a href="#connect_proxy_allow_managed_registration">`allow_managed_api_registration`</a> Allows managed proxies to be configured with services that are registered via the Agent HTTP API. Enabling this would allow anyone with permission to register a service to define a command to execute for the proxy. By default, this is false to protect against arbitrary process execution.
|
||||
|
|
|
@ -88,6 +88,7 @@ $ curl http://localhost:8500/v1/connect/ca/configuration
|
|||
{
|
||||
"Provider": "consul",
|
||||
"Config": {
|
||||
"LeafCertTTL": "72h",
|
||||
"RotationPeriod": "2160h"
|
||||
},
|
||||
"CreateIndex": 5,
|
||||
|
|
|
@ -53,6 +53,9 @@ is used if configuring in an agent configuration file.
|
|||
bootstrap with the ".consul" TLD. The cluster identifier can be found
|
||||
using the [CA List Roots endpoint](/api/connect/ca.html#list-ca-root-certificates).
|
||||
|
||||
There are also [common CA configuration options](/docs/agent/options.html#common-ca-config-options)
|
||||
that are supported by all CA providers.
|
||||
|
||||
## Specifying a Custom Private Key and Root Certificate
|
||||
|
||||
By default, a root certificate and private key will be automatically
|
||||
|
@ -69,6 +72,7 @@ $ curl localhost:8500/v1/connect/ca/configuration
|
|||
{
|
||||
"Provider": "consul",
|
||||
"Config": {
|
||||
"LeafCertTTL": "72h",
|
||||
"RotationPeriod": "2160h"
|
||||
},
|
||||
"CreateIndex": 5,
|
||||
|
@ -99,6 +103,7 @@ $ jq -n --arg key "$(cat root.key)" --arg cert "$(cat root.crt)" '
|
|||
{
|
||||
"Provider": "consul",
|
||||
"Config": {
|
||||
"LeafCertTTL": "72h",
|
||||
"PrivateKey": $key,
|
||||
"RootCert": $cert,
|
||||
"RotationPeriod": "2160h"
|
||||
|
@ -113,6 +118,7 @@ $ cat ca_config.json
|
|||
{
|
||||
"Provider": "consul",
|
||||
"Config": {
|
||||
"LeafCertTTL": "72h",
|
||||
"PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEArqiy1c3pbT3cSkjdEM1APALUareU...",
|
||||
"RootCert": "-----BEGIN CERTIFICATE-----\nMIIDijCCAnKgAwIBAgIJAOFZ66em1qC7MA0GCSqGSIb3...",
|
||||
"RotationPeriod": "2160h"
|
||||
|
|
Loading…
Reference in New Issue