connect/ca: Allow ForceWithoutCrossSigning for all providers
This allows setting ForceWithoutCrossSigning when reconfiguring the CA for any provider, in order to forcibly move to a new root in cases where the old provider isn't reachable or able to cross-sign for whatever reason.
This commit is contained in:
parent
320fcf4510
commit
1dee4173c1
|
@ -873,12 +873,13 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
|
|||
"You can try again with ForceWithoutCrossSigningSet but this may cause " +
|
||||
"disruption - see documentation for more.")
|
||||
}
|
||||
if !canXSign && args.Config.ForceWithoutCrossSigning {
|
||||
c.logger.Warn("current CA doesn't support cross signing but " +
|
||||
"CA reconfiguration forced anyway with ForceWithoutCrossSigning")
|
||||
if args.Config.ForceWithoutCrossSigning {
|
||||
c.logger.Warn("ForceWithoutCrossSigning set, CA reconfiguration skipping cross-signing")
|
||||
}
|
||||
|
||||
if canXSign {
|
||||
// If ForceWithoutCrossSigning wasn't set, attempt to have the old CA generate a
|
||||
// cross-signed intermediate.
|
||||
if canXSign && !args.Config.ForceWithoutCrossSigning {
|
||||
// Have the old provider cross-sign the new root
|
||||
xcCert, err := oldProvider.CrossSignCA(newRoot)
|
||||
if err != nil {
|
||||
|
|
|
@ -1305,3 +1305,130 @@ func TestLeader_Consul_BadCAConfigShouldntPreventLeaderEstablishment(t *testing.
|
|||
require.NotEmpty(t, rootsList.Roots)
|
||||
require.NotNil(t, activeRoot)
|
||||
}
|
||||
|
||||
func TestLeader_Consul_ForceWithoutCrossSigning(t *testing.T) {
|
||||
require := require.New(t)
|
||||
dir1, s1 := testServer(t)
|
||||
defer os.RemoveAll(dir1)
|
||||
defer s1.Shutdown()
|
||||
codec := rpcClient(t, s1)
|
||||
defer codec.Close()
|
||||
|
||||
waitForLeaderEstablishment(t, s1)
|
||||
|
||||
// Get the current root
|
||||
rootReq := &structs.DCSpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
}
|
||||
var rootList structs.IndexedCARoots
|
||||
require.Nil(msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", rootReq, &rootList))
|
||||
require.Len(rootList.Roots, 1)
|
||||
oldRoot := rootList.Roots[0]
|
||||
|
||||
// Update the provider config to use a new private key, which should
|
||||
// cause a rotation.
|
||||
_, newKey, err := connect.GeneratePrivateKey()
|
||||
require.NoError(err)
|
||||
newConfig := &structs.CAConfiguration{
|
||||
Provider: "consul",
|
||||
Config: map[string]interface{}{
|
||||
"LeafCertTTL": "500ms",
|
||||
"PrivateKey": newKey,
|
||||
"RootCert": "",
|
||||
"RotationPeriod": "2160h",
|
||||
"SkipValidate": true,
|
||||
},
|
||||
ForceWithoutCrossSigning: true,
|
||||
}
|
||||
{
|
||||
args := &structs.CARequest{
|
||||
Datacenter: "dc1",
|
||||
Config: newConfig,
|
||||
}
|
||||
var reply interface{}
|
||||
|
||||
require.NoError(msgpackrpc.CallWithCodec(codec, "ConnectCA.ConfigurationSet", args, &reply))
|
||||
}
|
||||
|
||||
// Old root should no longer be active.
|
||||
_, roots, err := s1.fsm.State().CARoots(nil)
|
||||
require.NoError(err)
|
||||
require.Len(roots, 2)
|
||||
for _, r := range roots {
|
||||
if r.ID == oldRoot.ID {
|
||||
require.False(r.Active)
|
||||
} else {
|
||||
require.True(r.Active)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLeader_Vault_ForceWithoutCrossSigning(t *testing.T) {
|
||||
ca.SkipIfVaultNotPresent(t)
|
||||
|
||||
require := require.New(t)
|
||||
testVault := ca.NewTestVaultServer(t)
|
||||
defer testVault.Stop()
|
||||
|
||||
_, s1 := testServerWithConfig(t, func(c *Config) {
|
||||
c.Build = "1.9.1"
|
||||
c.PrimaryDatacenter = "dc1"
|
||||
c.CAConfig = &structs.CAConfiguration{
|
||||
Provider: "vault",
|
||||
Config: map[string]interface{}{
|
||||
"Address": testVault.Addr,
|
||||
"Token": testVault.RootToken,
|
||||
"RootPKIPath": "pki-root/",
|
||||
"IntermediatePKIPath": "pki-intermediate/",
|
||||
},
|
||||
}
|
||||
})
|
||||
defer s1.Shutdown()
|
||||
codec := rpcClient(t, s1)
|
||||
defer codec.Close()
|
||||
|
||||
waitForLeaderEstablishment(t, s1)
|
||||
|
||||
// Get the current root
|
||||
rootReq := &structs.DCSpecificRequest{
|
||||
Datacenter: "dc1",
|
||||
}
|
||||
var rootList structs.IndexedCARoots
|
||||
require.Nil(msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", rootReq, &rootList))
|
||||
require.Len(rootList.Roots, 1)
|
||||
oldRoot := rootList.Roots[0]
|
||||
|
||||
// Update the provider config to use a new PKI path, which should
|
||||
// cause a rotation.
|
||||
newConfig := &structs.CAConfiguration{
|
||||
Provider: "vault",
|
||||
Config: map[string]interface{}{
|
||||
"Address": testVault.Addr,
|
||||
"Token": testVault.RootToken,
|
||||
"RootPKIPath": "pki-root-2/",
|
||||
"IntermediatePKIPath": "pki-intermediate/",
|
||||
},
|
||||
ForceWithoutCrossSigning: true,
|
||||
}
|
||||
{
|
||||
args := &structs.CARequest{
|
||||
Datacenter: "dc1",
|
||||
Config: newConfig,
|
||||
}
|
||||
var reply interface{}
|
||||
|
||||
require.NoError(msgpackrpc.CallWithCodec(codec, "ConnectCA.ConfigurationSet", args, &reply))
|
||||
}
|
||||
|
||||
// Old root should no longer be active.
|
||||
_, roots, err := s1.fsm.State().CARoots(nil)
|
||||
require.NoError(err)
|
||||
require.Len(roots, 2)
|
||||
for _, r := range roots {
|
||||
if r.ID == oldRoot.ID {
|
||||
require.False(r.Active)
|
||||
} else {
|
||||
require.True(r.Active)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,14 @@ type CAConfig struct {
|
|||
// configuration is an error.
|
||||
State map[string]string
|
||||
|
||||
// ForceWithoutCrossSigning indicates that the CA reconfiguration should go
|
||||
// ahead even if the current CA is unable to cross sign certificates. This
|
||||
// risks temporary connection failures during the rollout as new leafs will be
|
||||
// rejected by proxies that have not yet observed the new root cert but is the
|
||||
// only option if a CA that doesn't support cross signing needs to be
|
||||
// reconfigured or mirated away from.
|
||||
ForceWithoutCrossSigning bool
|
||||
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
|
|
@ -24,13 +24,20 @@ type cmd struct {
|
|||
help string
|
||||
|
||||
// flags
|
||||
configFile flags.StringValue
|
||||
configFile flags.StringValue
|
||||
forceWithoutCrossSigning bool
|
||||
}
|
||||
|
||||
func (c *cmd) init() {
|
||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
c.flags.Var(&c.configFile, "config-file",
|
||||
"The path to the config file to use.")
|
||||
c.flags.BoolVar(&c.forceWithoutCrossSigning, "force-without-cross-signing", false,
|
||||
"Indicates that the CA reconfiguration should go ahead even if the current "+
|
||||
"CA is unable to cross sign certificates. This risks temporary connection "+
|
||||
"failures during the rollout as new leafs will be rejected by proxies that "+
|
||||
"have not yet observed the new root cert but is the only option if a CA that "+
|
||||
"doesn't support cross signing needs to be reconfigured or mirated away from.")
|
||||
|
||||
c.http = &flags.HTTPFlags{}
|
||||
flags.Merge(c.flags, c.http.ClientFlags())
|
||||
|
@ -70,6 +77,7 @@ func (c *cmd) Run(args []string) int {
|
|||
c.UI.Error(fmt.Sprintf("Error parsing config file: %s", err))
|
||||
return 1
|
||||
}
|
||||
config.ForceWithoutCrossSigning = c.forceWithoutCrossSigning
|
||||
|
||||
// Set the new configuration.
|
||||
if _, err := client.Connect().CASetConfig(&config, nil); err != nil {
|
||||
|
|
|
@ -176,7 +176,7 @@ The table below shows this endpoint's support for
|
|||
providers, see [Provider Config](/docs/connect/ca).
|
||||
|
||||
- `ForceWithoutCrossSigning` `(bool: <optional>)` - Indicates that the CA change
|
||||
should be force to complete even if the current CA doesn't support cross
|
||||
should be forced to complete even if the current CA doesn't support cross
|
||||
signing. Changing root without cross-signing may cause temporary connection
|
||||
failures until the rollout completes. See [Forced Rotation Without
|
||||
Cross-Signing](/docs/connect/ca#forced-rotation-without-cross-signing)
|
||||
|
|
|
@ -82,6 +82,13 @@ Usage: `consul connect ca set-config [options]`
|
|||
The format of this config file matches the request payload documented in the
|
||||
[Update CA Configuration API](/api/connect/ca#update-ca-configuration).
|
||||
|
||||
- `-force-without-cross-signing` `(bool: <optional>)` - Indicates that the CA change
|
||||
should be forced to complete even if the current CA doesn't support cross
|
||||
signing. Changing root without cross-signing may cause temporary connection
|
||||
failures until the rollout completes. See [Forced Rotation Without
|
||||
Cross-Signing](/docs/connect/ca#forced-rotation-without-cross-signing)
|
||||
for more detail.
|
||||
|
||||
The output looks like this:
|
||||
|
||||
```
|
||||
|
|
Loading…
Reference in a new issue