Skip to content

Commit

Permalink
Loosen Vault CA provider validation for RootPKIPath
Browse files Browse the repository at this point in the history
Update Vault CA provider documentation
  • Loading branch information
Chris S. Kim committed Jun 21, 2023
1 parent 9a1f688 commit 81b2035
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 26 deletions.
2 changes: 1 addition & 1 deletion agent/config/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1473,7 +1473,7 @@ func (b *builder) validate(rt RuntimeConfig) error {
return err
}
case structs.VaultCAProvider:
if _, err := ca.ParseVaultCAConfig(rt.ConnectCAConfig); err != nil {
if _, err := ca.ParseVaultCAConfig(rt.ConnectCAConfig, rt.PrimaryDatacenter == rt.Datacenter); err != nil {
return err
}
case structs.AWSCAProvider:
Expand Down
2 changes: 1 addition & 1 deletion agent/connect/ca/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func TestStructs_CAConfiguration_MsgpackEncodeDecode(t *testing.T) {
TLSSkipVerify: true,
},
parseFunc: func(t *testing.T, raw map[string]interface{}) interface{} {
config, err := ParseVaultCAConfig(raw)
config, err := ParseVaultCAConfig(raw, true)
require.NoError(t, err)
return config
},
Expand Down
14 changes: 7 additions & 7 deletions agent/connect/ca/provider_vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func vaultTLSConfig(config *structs.VaultCAProviderConfig) *vaultapi.TLSConfig {
// Configure sets up the provider using the given configuration.
// Configure supports being called multiple times to re-configure the provider.
func (v *VaultProvider) Configure(cfg ProviderConfig) error {
config, err := ParseVaultCAConfig(cfg.RawConfig)
config, err := ParseVaultCAConfig(cfg.RawConfig, v.isPrimary)
if err != nil {
return err
}
Expand Down Expand Up @@ -192,11 +192,11 @@ func (v *VaultProvider) Configure(cfg ProviderConfig) error {
}

func (v *VaultProvider) ValidateConfigUpdate(prevRaw, nextRaw map[string]interface{}) error {
prev, err := ParseVaultCAConfig(prevRaw)
prev, err := ParseVaultCAConfig(prevRaw, v.isPrimary)
if err != nil {
return fmt.Errorf("failed to parse existing CA config: %w", err)
}
next, err := ParseVaultCAConfig(nextRaw)
next, err := ParseVaultCAConfig(nextRaw, v.isPrimary)
if err != nil {
return fmt.Errorf("failed to parse new CA config: %w", err)
}
Expand Down Expand Up @@ -800,7 +800,7 @@ func (v *VaultProvider) Cleanup(providerTypeChange bool, otherConfig map[string]
v.Stop()

if !providerTypeChange {
newConfig, err := ParseVaultCAConfig(otherConfig)
newConfig, err := ParseVaultCAConfig(otherConfig, v.isPrimary)
if err != nil {
return err
}
Expand Down Expand Up @@ -900,7 +900,7 @@ func (v *VaultProvider) autotidyIssuers(path string) (bool, string) {
return tidySet, errStr
}

func ParseVaultCAConfig(raw map[string]interface{}) (*structs.VaultCAProviderConfig, error) {
func ParseVaultCAConfig(raw map[string]interface{}, isPrimary bool) (*structs.VaultCAProviderConfig, error) {
config := structs.VaultCAProviderConfig{
CommonCAProviderConfig: defaultCommonConfig(),
}
Expand Down Expand Up @@ -931,10 +931,10 @@ func ParseVaultCAConfig(raw map[string]interface{}) (*structs.VaultCAProviderCon
return nil, fmt.Errorf("only one of Vault token or Vault auth method can be provided, but not both")
}

if config.RootPKIPath == "" {
if isPrimary && config.RootPKIPath == "" {
return nil, fmt.Errorf("must provide a valid path to a root PKI backend")
}
if !strings.HasSuffix(config.RootPKIPath, "/") {
if config.RootPKIPath != "" && !strings.HasSuffix(config.RootPKIPath, "/") {
config.RootPKIPath += "/"
}

Expand Down
18 changes: 15 additions & 3 deletions agent/connect/ca/provider_vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func TestVaultCAProvider_ParseVaultCAConfig(t *testing.T) {
cases := map[string]struct {
rawConfig map[string]interface{}
expConfig *structs.VaultCAProviderConfig
isPrimary bool
expError string
}{
"no token and no auth method provided": {
Expand All @@ -70,15 +71,26 @@ func TestVaultCAProvider_ParseVaultCAConfig(t *testing.T) {
rawConfig: map[string]interface{}{"Token": "test", "AuthMethod": map[string]interface{}{"Type": "test"}},
expError: "only one of Vault token or Vault auth method can be provided, but not both",
},
"no root PKI path": {
rawConfig: map[string]interface{}{"Token": "test"},
"primary no root PKI path": {
rawConfig: map[string]interface{}{"Token": "test", "IntermediatePKIPath": "test"},
isPrimary: true,
expError: "must provide a valid path to a root PKI backend",
},
"secondary no root PKI path": {
rawConfig: map[string]interface{}{"Token": "test", "IntermediatePKIPath": "test"},
isPrimary: false,
expConfig: &structs.VaultCAProviderConfig{
CommonCAProviderConfig: defaultCommonConfig(),
Token: "test",
IntermediatePKIPath: "test/",
},
},
"no root intermediate path": {
rawConfig: map[string]interface{}{"Token": "test", "RootPKIPath": "test"},
expError: "must provide a valid path for the intermediate PKI backend",
},
"adds a slash to RootPKIPath and IntermediatePKIPath": {
isPrimary: true,
rawConfig: map[string]interface{}{"Token": "test", "RootPKIPath": "test", "IntermediatePKIPath": "test"},
expConfig: &structs.VaultCAProviderConfig{
CommonCAProviderConfig: defaultCommonConfig(),
Expand All @@ -91,7 +103,7 @@ func TestVaultCAProvider_ParseVaultCAConfig(t *testing.T) {

for name, c := range cases {
t.Run(name, func(t *testing.T) {
config, err := ParseVaultCAConfig(c.rawConfig)
config, err := ParseVaultCAConfig(c.rawConfig, c.isPrimary)
if c.expError != "" {
require.EqualError(t, err, c.expError)
} else {
Expand Down
33 changes: 19 additions & 14 deletions website/content/docs/connect/ca/vault.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@ description: >-

# Vault as a Service Mesh Certificate Authority

You can configure Consul to use [Vault](https://www.vaultproject.io/) as the certificate authority (CA) so that Vault can manage and sign certificates distributed to services in the mesh.
The Vault CA provider uses the [Vault PKI secrets engine](/vault/docs/secrets/pki) to generate and sign certificates.
You can configure Consul to use [Vault](https://www.vaultproject.io/) as the certificate authority (CA) so that Vault can manage and sign certificates distributed to services in the mesh.
The Vault CA provider uses the [Vault PKI secrets engine](/vault/docs/secrets/pki) to generate and sign certificates.
This page describes how configure the Vault CA provider.

> **Tutorial:** Complete the [Vault as Consul Service Mesh Certification Authority](/consul/tutorials/vault-secure/vault-pki-consul-connect-ca) tutorial for hands-on guidance on how to configure Vault as the Consul service mesh certification authority.
## Requirements

- Vault 0.10.3 or higher

~> **Compatibility note:** If you use Vault 1.11.0+ as Consul's service mesh CA, versions of Consul released before Dec 13, 2022 will develop an issue with Consul control plane or service mesh communication ([GH-15525](https://github.com/hashicorp/consul/pull/15525)). Use or upgrade to a [Consul version that includes the fix](https://support.hashicorp.com/hc/en-us/articles/11308460105491#01GMC24E6PPGXMRX8DMT4HZYTW) to avoid this problem.

## Recommendations

- Refer to [Service Mesh Certificate Authority Overview](/consul/docs/connect/ca) for important background information about how Consul manages certificates with configurable CA providers.

- Vault 0.10.3 to 1.10.x.
- For best performance and resiliency, every datacenter should have a Vault cluster local to its Consul cluster.

~> **Compatibility note:** If you use Vault 1.11.0+ as Consul's service mesh CA, versions of Consul released before Dec 13, 2022 will develop an issue with Consul control plane or service mesh communication ([GH-15525](https://github.com/hashicorp/consul/pull/15525)). Use or upgrade to a [Consul version that includes the fix](https://support.hashicorp.com/hc/en-us/articles/11308460105491#01GMC24E6PPGXMRX8DMT4HZYTW) to avoid this problem.
- In WAN-federated environments, Vault Enterprise users using [performance secondaries](/vault/docs/enterprise/replication#performance-replication) in their secondary datacenters
are recommended to use [`local`](/vault/docs/enterprise/replication#local) mounts for their [`intermediate_pki_path`](/consul/docs/connect/ca/vault#intermediatepkipath).

## Enable Vault as the CA

Expand All @@ -28,7 +35,7 @@ and including the required provider configuration options.
You can provide the CA configuration in the server agents' configuration file
or in the body of a `PUT` request to the
[`/connect/ca/configuration`](/consul/api-docs/connect/ca#update-ca-configuration) API endpoint.
Refer to the [Configuration Reference](#configuration-reference) for details about configuration options and for example use cases.
Refer to the [Configuration Reference](#configuration-reference) for details about configuration options and for example use cases.

The following example shows the required configurations for a default implementation:

Expand Down Expand Up @@ -75,7 +82,7 @@ connect {
You can specify the following configuration options.
Note that a configuration option's name may differ between API calls and the agent configuration file.
The first key refers to the option name for use in API calls.
The key after the slash refers to the corresponding option name in the agent configuration file.
The key after the slash refers to the corresponding option name in the agent configuration file.

- `Address` / `address` (`string: <required>`) - The address of the Vault
server.
Expand Down Expand Up @@ -104,7 +111,8 @@ The key after the slash refers to the corresponding option name in the agent con
Only the authentication related fields (for example, JWT's `path` and `role`) are supported. The optional management fields (for example: `remove_jwt_after_reading`) are not supported.

- `RootPKIPath` / `root_pki_path` (`string: <required>`) - The path to
a PKI secrets engine for the root certificate.
a PKI secrets engine for the root certificate. Required for primary
datacenters. Secondary datacenters do not use this path.

If the path does not
exist, Consul will mount a new PKI secrets engine at the specified path with the
Expand All @@ -114,9 +122,6 @@ The key after the slash refers to the corresponding option name in the agent con
the root certificate TTL was set to 8760 hour, or 1 year, and was not configurable.
The root certificate will expire at the end of the specified period.

When WAN Federation is enabled, each secondary datacenter must use the same Vault cluster and share the same `root_pki_path`
with the primary datacenter.

To use an intermediate certificate as the primary CA in Consul, initialize the
`RootPKIPath` in Vault with a PEM bundle. The first certificate in the bundle
must be the intermediate certificate that Consul will use as the primary CA.
Expand Down Expand Up @@ -242,7 +247,7 @@ Then, attach the following Vault ACL policy to the CA provider's
path "/<root_pki_path>/" {
capabilities = [ "read" ]
}
path "/<root_pki_path>/root/sign-intermediate" {
capabilities = [ "update" ]
}
Expand All @@ -268,7 +273,7 @@ Then, attach the following Vault ACL policy to the CA provider's
capabilities = [ "read" ]
}
```

</CodeBlockConfig>

#### Define a policy for Consul-managed PKI paths ((#consul-managed-pki-paths))
Expand Down Expand Up @@ -329,7 +334,7 @@ Then, attach the following Vault ACL policy to the CA provider's
capabilities = [ "read" ]
}
```

</CodeBlockConfig>

#### Additional Vault ACL policies for sensitive operations
Expand All @@ -340,7 +345,7 @@ following CA provider configuration changes:
- Changing the `RootPKIPath`

Those configuration modifications trigger a root CA change that requires an
extremely privileged root cross-sign operation.
extremely privileged root cross-sign operation.
For that operation to succeed, the CA provider's [Vault token](#token) or
[auth method](#authmethod) must contain the following rule:

Expand Down

0 comments on commit 81b2035

Please sign in to comment.