Merge pull request #9672 from hashicorp/ca-force-skip-xc
connect/ca: Allow ForceWithoutCrossSigning for all providers
This commit is contained in:
commit
237b41ac8f
4
.changelog/9672.txt
Normal file
4
.changelog/9672.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
```release-note:improvement
|
||||||
|
cli: added a `-force-without-cross-signing` flag to the `ca set-config` command.
|
||||||
|
connect/ca: The ForceWithoutCrossSigning field will now work as expected for CA providers that support cross signing.
|
||||||
|
```
|
|
@ -887,12 +887,13 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
|
||||||
"You can try again with ForceWithoutCrossSigningSet but this may cause " +
|
"You can try again with ForceWithoutCrossSigningSet but this may cause " +
|
||||||
"disruption - see documentation for more.")
|
"disruption - see documentation for more.")
|
||||||
}
|
}
|
||||||
if !canXSign && args.Config.ForceWithoutCrossSigning {
|
if args.Config.ForceWithoutCrossSigning {
|
||||||
c.logger.Warn("current CA doesn't support cross signing but " +
|
c.logger.Warn("ForceWithoutCrossSigning set, CA reconfiguration skipping cross-signing")
|
||||||
"CA reconfiguration forced anyway with ForceWithoutCrossSigning")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Have the old provider cross-sign the new root
|
||||||
xcCert, err := oldProvider.CrossSignCA(newRoot)
|
xcCert, err := oldProvider.CrossSignCA(newRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1410,3 +1410,130 @@ func TestLeader_Consul_BadCAConfigShouldntPreventLeaderEstablishment(t *testing.
|
||||||
require.NotEmpty(t, rootsList.Roots)
|
require.NotEmpty(t, rootsList.Roots)
|
||||||
require.NotNil(t, activeRoot)
|
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.
|
// configuration is an error.
|
||||||
State map[string]string
|
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
|
CreateIndex uint64
|
||||||
ModifyIndex uint64
|
ModifyIndex uint64
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,20 @@ type cmd struct {
|
||||||
help string
|
help string
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
configFile flags.StringValue
|
configFile flags.StringValue
|
||||||
|
forceWithoutCrossSigning bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmd) init() {
|
func (c *cmd) init() {
|
||||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
c.flags.Var(&c.configFile, "config-file",
|
c.flags.Var(&c.configFile, "config-file",
|
||||||
"The path to the config file to use.")
|
"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{}
|
c.http = &flags.HTTPFlags{}
|
||||||
flags.Merge(c.flags, c.http.ClientFlags())
|
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))
|
c.UI.Error(fmt.Sprintf("Error parsing config file: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
config.ForceWithoutCrossSigning = c.forceWithoutCrossSigning
|
||||||
|
|
||||||
// Set the new configuration.
|
// Set the new configuration.
|
||||||
if _, err := client.Connect().CASetConfig(&config, nil); err != nil {
|
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).
|
providers, see [Provider Config](/docs/connect/ca).
|
||||||
|
|
||||||
- `ForceWithoutCrossSigning` `(bool: <optional>)` - Indicates that the CA change
|
- `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
|
signing. Changing root without cross-signing may cause temporary connection
|
||||||
failures until the rollout completes. See [Forced Rotation Without
|
failures until the rollout completes. See [Forced Rotation Without
|
||||||
Cross-Signing](/docs/connect/ca#forced-rotation-without-cross-signing)
|
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
|
The format of this config file matches the request payload documented in the
|
||||||
[Update CA Configuration API](/api/connect/ca#update-ca-configuration).
|
[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:
|
The output looks like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in a new issue