mirror of https://github.com/status-im/consul.git
Merge pull request #14516 from hashicorp/ca-ttl-fixes
Fix inconsistent TTL behavior in CA providers
This commit is contained in:
commit
60cee76746
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
ca: Fixed a bug with the Vault CA provider where the intermediate PKI mount and leaf cert role were not being updated when the CA configuration was changed.
|
||||||
|
```
|
|
@ -66,11 +66,10 @@ type VaultProvider struct {
|
||||||
|
|
||||||
stopWatcher func()
|
stopWatcher func()
|
||||||
|
|
||||||
isPrimary bool
|
isPrimary bool
|
||||||
clusterID string
|
clusterID string
|
||||||
spiffeID *connect.SpiffeIDSigning
|
spiffeID *connect.SpiffeIDSigning
|
||||||
setupIntermediatePKIPathDone bool
|
logger hclog.Logger
|
||||||
logger hclog.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVaultProvider(logger hclog.Logger) *VaultProvider {
|
func NewVaultProvider(logger hclog.Logger) *VaultProvider {
|
||||||
|
@ -174,6 +173,11 @@ func (v *VaultProvider) Configure(cfg ProviderConfig) error {
|
||||||
go v.renewToken(ctx, lifetimeWatcher)
|
go v.renewToken(ctx, lifetimeWatcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the intermediate (managed) PKI mount and role
|
||||||
|
if err := v.setupIntermediatePKIPath(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,8 +367,8 @@ func (v *VaultProvider) GenerateIntermediateCSR() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VaultProvider) setupIntermediatePKIPath() error {
|
func (v *VaultProvider) setupIntermediatePKIPath() error {
|
||||||
if v.setupIntermediatePKIPathDone {
|
mountConfig := vaultapi.MountConfigInput{
|
||||||
return nil
|
MaxLeaseTTL: v.config.IntermediateCertTTL.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := v.getCA(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath)
|
_, err := v.getCA(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath)
|
||||||
|
@ -373,9 +377,7 @@ func (v *VaultProvider) setupIntermediatePKIPath() error {
|
||||||
err := v.mountNamespaced(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath, &vaultapi.MountInput{
|
err := v.mountNamespaced(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath, &vaultapi.MountInput{
|
||||||
Type: "pki",
|
Type: "pki",
|
||||||
Description: "intermediate CA backend for Consul Connect",
|
Description: "intermediate CA backend for Consul Connect",
|
||||||
Config: vaultapi.MountConfigInput{
|
Config: mountConfig,
|
||||||
MaxLeaseTTL: v.config.IntermediateCertTTL.String(),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -383,39 +385,28 @@ func (v *VaultProvider) setupIntermediatePKIPath() error {
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
err := v.tuneMountNamespaced(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath, &mountConfig)
|
||||||
// Create the role for issuing leaf certs if it doesn't exist yet
|
|
||||||
rolePath := v.config.IntermediatePKIPath + "roles/" + VaultCALeafCertRole
|
|
||||||
role, err := v.readNamespaced(v.config.IntermediatePKINamespace, rolePath)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if role == nil {
|
|
||||||
_, err := v.writeNamespaced(v.config.IntermediatePKINamespace, rolePath, map[string]interface{}{
|
|
||||||
"allow_any_name": true,
|
|
||||||
"allowed_uri_sans": "spiffe://*",
|
|
||||||
"key_type": "any",
|
|
||||||
"max_ttl": v.config.LeafCertTTL.String(),
|
|
||||||
"no_store": true,
|
|
||||||
"require_cn": false,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v.setupIntermediatePKIPathDone = true
|
|
||||||
return nil
|
// Create the role for issuing leaf certs if it doesn't exist yet
|
||||||
|
rolePath := v.config.IntermediatePKIPath + "roles/" + VaultCALeafCertRole
|
||||||
|
_, err = v.writeNamespaced(v.config.IntermediatePKINamespace, rolePath, map[string]interface{}{
|
||||||
|
"allow_any_name": true,
|
||||||
|
"allowed_uri_sans": "spiffe://*",
|
||||||
|
"key_type": "any",
|
||||||
|
"max_ttl": v.config.LeafCertTTL.String(),
|
||||||
|
"no_store": true,
|
||||||
|
"require_cn": false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VaultProvider) generateIntermediateCSR() (string, error) {
|
func (v *VaultProvider) generateIntermediateCSR() (string, error) {
|
||||||
err := v.setupIntermediatePKIPath()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a new intermediate CSR for the root to sign.
|
// Generate a new intermediate CSR for the root to sign.
|
||||||
uid, err := connect.CompactUID()
|
uid, err := connect.CompactUID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -465,10 +456,6 @@ func (v *VaultProvider) SetIntermediate(intermediatePEM, rootPEM string) error {
|
||||||
|
|
||||||
// ActiveIntermediate returns the current intermediate certificate.
|
// ActiveIntermediate returns the current intermediate certificate.
|
||||||
func (v *VaultProvider) ActiveIntermediate() (string, error) {
|
func (v *VaultProvider) ActiveIntermediate() (string, error) {
|
||||||
if err := v.setupIntermediatePKIPath(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
cert, err := v.getCA(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath)
|
cert, err := v.getCA(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath)
|
||||||
|
|
||||||
// This error is expected when calling initializeSecondaryCA for the
|
// This error is expected when calling initializeSecondaryCA for the
|
||||||
|
@ -737,6 +724,19 @@ func (v *VaultProvider) mountNamespaced(namespace, path string, mountInfo *vault
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *VaultProvider) tuneMountNamespaced(namespace, path string, mountConfig *vaultapi.MountConfigInput) error {
|
||||||
|
defer v.setNamespace(namespace)()
|
||||||
|
r := v.client.NewRequest("POST", fmt.Sprintf("/v1/sys/mounts/%s/tune", path))
|
||||||
|
if err := r.SetJSONBody(mountConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp, err := v.client.RawRequest(r)
|
||||||
|
if resp != nil {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (v *VaultProvider) unmountNamespaced(namespace, path string) error {
|
func (v *VaultProvider) unmountNamespaced(namespace, path string) error {
|
||||||
defer v.setNamespace(namespace)()
|
defer v.setNamespace(namespace)()
|
||||||
r := v.client.NewRequest("DELETE", fmt.Sprintf("/v1/sys/mounts/%s", path))
|
r := v.client.NewRequest("DELETE", fmt.Sprintf("/v1/sys/mounts/%s", path))
|
||||||
|
|
|
@ -19,6 +19,16 @@ import (
|
||||||
"github.com/hashicorp/consul/sdk/testutil/retry"
|
"github.com/hashicorp/consul/sdk/testutil/retry"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const pkiTestPolicy = `
|
||||||
|
path "sys/mounts/*"
|
||||||
|
{
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
||||||
|
}
|
||||||
|
path "pki-intermediate/*"
|
||||||
|
{
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
||||||
|
}`
|
||||||
|
|
||||||
func TestVaultCAProvider_ParseVaultCAConfig(t *testing.T) {
|
func TestVaultCAProvider_ParseVaultCAConfig(t *testing.T) {
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
rawConfig map[string]interface{}
|
rawConfig map[string]interface{}
|
||||||
|
@ -653,7 +663,7 @@ func TestVaultProvider_ConfigureWithAuthMethod(t *testing.T) {
|
||||||
authMethodType: "userpass",
|
authMethodType: "userpass",
|
||||||
configureAuthMethodFunc: func(t *testing.T, vaultClient *vaultapi.Client) map[string]interface{} {
|
configureAuthMethodFunc: func(t *testing.T, vaultClient *vaultapi.Client) map[string]interface{} {
|
||||||
_, err := vaultClient.Logical().Write("/auth/userpass/users/test",
|
_, err := vaultClient.Logical().Write("/auth/userpass/users/test",
|
||||||
map[string]interface{}{"password": "foo", "policies": "admins"})
|
map[string]interface{}{"password": "foo", "policies": "pki"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"Type": "userpass",
|
"Type": "userpass",
|
||||||
|
@ -667,7 +677,8 @@ func TestVaultProvider_ConfigureWithAuthMethod(t *testing.T) {
|
||||||
{
|
{
|
||||||
authMethodType: "approle",
|
authMethodType: "approle",
|
||||||
configureAuthMethodFunc: func(t *testing.T, vaultClient *vaultapi.Client) map[string]interface{} {
|
configureAuthMethodFunc: func(t *testing.T, vaultClient *vaultapi.Client) map[string]interface{} {
|
||||||
_, err := vaultClient.Logical().Write("auth/approle/role/my-role", nil)
|
_, err := vaultClient.Logical().Write("auth/approle/role/my-role",
|
||||||
|
map[string]interface{}{"token_policies": "pki"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
resp, err := vaultClient.Logical().Read("auth/approle/role/my-role/role-id")
|
resp, err := vaultClient.Logical().Read("auth/approle/role/my-role/role-id")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -695,6 +706,9 @@ func TestVaultProvider_ConfigureWithAuthMethod(t *testing.T) {
|
||||||
err := testVault.Client().Sys().EnableAuthWithOptions(c.authMethodType, &vaultapi.EnableAuthOptions{Type: c.authMethodType})
|
err := testVault.Client().Sys().EnableAuthWithOptions(c.authMethodType, &vaultapi.EnableAuthOptions{Type: c.authMethodType})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = testVault.Client().Sys().PutPolicy("pki", pkiTestPolicy)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
authMethodConf := c.configureAuthMethodFunc(t, testVault.Client())
|
authMethodConf := c.configureAuthMethodFunc(t, testVault.Client())
|
||||||
|
|
||||||
conf := map[string]interface{}{
|
conf := map[string]interface{}{
|
||||||
|
@ -726,11 +740,18 @@ func TestVaultProvider_RotateAuthMethodToken(t *testing.T) {
|
||||||
|
|
||||||
testVault := NewTestVaultServer(t)
|
testVault := NewTestVaultServer(t)
|
||||||
|
|
||||||
err := testVault.Client().Sys().EnableAuthWithOptions("approle", &vaultapi.EnableAuthOptions{Type: "approle"})
|
err := testVault.Client().Sys().PutPolicy("pki", pkiTestPolicy)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = testVault.Client().Sys().EnableAuthWithOptions("approle", &vaultapi.EnableAuthOptions{Type: "approle"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = testVault.Client().Logical().Write("auth/approle/role/my-role",
|
_, err = testVault.Client().Logical().Write("auth/approle/role/my-role",
|
||||||
map[string]interface{}{"token_ttl": "2s", "token_explicit_max_ttl": "2s"})
|
map[string]interface{}{
|
||||||
|
"token_ttl": "2s",
|
||||||
|
"token_explicit_max_ttl": "2s",
|
||||||
|
"token_policies": "pki",
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
resp, err := testVault.Client().Logical().Read("auth/approle/role/my-role/role-id")
|
resp, err := testVault.Client().Logical().Read("auth/approle/role/my-role/role-id")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -382,9 +382,12 @@ func (c *CAConfiguration) GetCommonConfig() (*CommonCAProviderConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommonCAProviderConfig struct {
|
type CommonCAProviderConfig struct {
|
||||||
LeafCertTTL time.Duration
|
LeafCertTTL time.Duration
|
||||||
|
RootCertTTL time.Duration
|
||||||
|
|
||||||
|
// IntermediateCertTTL is only valid in the primary datacenter, and determines
|
||||||
|
// the duration that any signed intermediates are valid for.
|
||||||
IntermediateCertTTL time.Duration
|
IntermediateCertTTL time.Duration
|
||||||
RootCertTTL time.Duration
|
|
||||||
|
|
||||||
SkipValidate bool
|
SkipValidate bool
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,15 @@ The following configuration options are supported by all CA providers:
|
||||||
|
|
||||||
For the Vault provider, this value is only used if the backend is not initialized at first.
|
For the Vault provider, this value is only used if the backend is not initialized at first.
|
||||||
|
|
||||||
|
- `IntermediateCertTTL` / `intermediate_cert_ttl` (`duration: "8760h"`) The time to live (TTL) for
|
||||||
|
any intermediate certificates signed by root certificate of the primary datacenter. *This field is only
|
||||||
|
valid in the primary datacenter*.
|
||||||
|
Defaults to 1 year as `8760h`.
|
||||||
|
|
||||||
|
This setting applies to all Consul CA providers.
|
||||||
|
|
||||||
|
For the Vault provider, this value is only used if the backend is not initialized at first.
|
||||||
|
|
||||||
- `PrivateKeyType` / `private_key_type` (`string: "ec"`) - The type of key to generate
|
- `PrivateKeyType` / `private_key_type` (`string: "ec"`) - The type of key to generate
|
||||||
for this CA. This is only used when the provider is generating a new key. If
|
for this CA. This is only used when the provider is generating a new key. If
|
||||||
`private_key` is set for the Consul provider, or existing root or intermediate
|
`private_key` is set for the Consul provider, or existing root or intermediate
|
||||||
|
|
Loading…
Reference in New Issue