Skip to content

Commit

Permalink
Support vault namespaces in connect CA
Browse files Browse the repository at this point in the history
Follow on to some missed items from #12655

From an internal ticket "Support standard "Vault namespace in the
path" semantics for Connect Vault CA Provider"

Vault allows the namespace to be specified as a prefix in the path of
a PKI definition, but our usage of the Vault API includes calls that
don't support a namespaced key. In particular the sys.* family of
calls simply appends the key, instead of prefixing the namespace in
front of the path.

Unfortunately it is difficult to reliably parse a path with a
namespace; only vault knows what namespaces are present, and the '/'
separator can be inside a key name, as well as separating path
elements. This is in use in the wild; for example
'dc1/intermediate-key' is a relatively common naming schema.

Instead we add two new fields: RootPKINamespace and
IntermediatePKINamespace, which are the absolute namespace paths
'prefixed' in front of the respective PKI Paths.

Signed-off-by: Mark Anderson <[email protected]>
  • Loading branch information
markan committed May 4, 2022
1 parent 8ebb515 commit 23ef36b
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 19 deletions.
22 changes: 12 additions & 10 deletions agent/config/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,16 +697,18 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
"intermediate_cert_ttl": "IntermediateCertTTL",

// Vault CA config
"address": "Address",
"token": "Token",
"root_pki_path": "RootPKIPath",
"intermediate_pki_path": "IntermediatePKIPath",
"ca_file": "CAFile",
"ca_path": "CAPath",
"cert_file": "CertFile",
"key_file": "KeyFile",
"tls_server_name": "TLSServerName",
"tls_skip_verify": "TLSSkipVerify",
"address": "Address",
"token": "Token",
"root_pki_path": "RootPKIPath",
"root_pki_namespace": "RootPKINamespace",
"intermediate_pki_path": "IntermediatePKIPath",
"intermediate_pki_namespace": "IntermediatePKINamespace",
"ca_file": "CAFile",
"ca_path": "CAPath",
"cert_file": "CertFile",
"key_file": "KeyFile",
"tls_server_name": "TLSServerName",
"tls_skip_verify": "TLSSkipVerify",

// AWS CA config
"existing_arn": "ExistingARN",
Expand Down
46 changes: 42 additions & 4 deletions agent/connect/ca/provider_vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ func (v *VaultProvider) GenerateRoot() (RootResult, error) {
rootPEM, err := v.getCA(v.config.RootPKIPath)
switch err {
case ErrBackendNotMounted:
err := v.client.Sys().Mount(v.config.RootPKIPath, &vaultapi.MountInput{

err := v.mountNamespaced(v.config.RootPKINamespace, v.config.RootPKIPath, &vaultapi.MountInput{
Type: "pki",
Description: "root CA backend for Consul Connect",
Config: vaultapi.MountConfigInput{
Expand Down Expand Up @@ -361,14 +362,13 @@ func (v *VaultProvider) setupIntermediatePKIPath() error {
_, err := v.getCA(v.config.IntermediatePKIPath)
if err != nil {
if err == ErrBackendNotMounted {
err := v.client.Sys().Mount(v.config.IntermediatePKIPath, &vaultapi.MountInput{
err := v.mountNamespaced(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath, &vaultapi.MountInput{
Type: "pki",
Description: "intermediate CA backend for Consul Connect",
Config: vaultapi.MountConfigInput{
MaxLeaseTTL: v.config.IntermediateCertTTL.String(),
},
})

if err != nil {
return err
}
Expand All @@ -380,6 +380,7 @@ func (v *VaultProvider) setupIntermediatePKIPath() error {
// Create the role for issuing leaf certs if it doesn't exist yet
rolePath := v.config.IntermediatePKIPath + "roles/" + VaultCALeafCertRole
role, err := v.client.Logical().Read(rolePath)

if err != nil {
return err
}
Expand All @@ -392,6 +393,7 @@ func (v *VaultProvider) setupIntermediatePKIPath() error {
"no_store": true,
"require_cn": false,
})

if err != nil {
return err
}
Expand Down Expand Up @@ -691,7 +693,7 @@ func (v *VaultProvider) Cleanup(providerTypeChange bool, otherConfig map[string]
}
}

err := v.client.Sys().Unmount(v.config.IntermediatePKIPath)
err := v.unmountNamespaced(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath)

switch err {
case ErrBackendNotMounted, ErrBackendNotInitialized:
Expand All @@ -709,6 +711,42 @@ func (v *VaultProvider) Stop() {

func (v *VaultProvider) PrimaryUsesIntermediate() {}

// We use raw path here
func (v *VaultProvider) mountNamespaced(namespace, path string, mountInfo *vaultapi.MountInput) error {
fullPath := makePathHelper(namespace, path)

r := v.client.NewRequest("POST", fullPath)
if err := r.SetJSONBody(mountInfo); 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 {
fullPath := makePathHelper(namespace, path)

r := v.client.NewRequest("DELETE", fullPath)
resp, err := v.client.RawRequest(r)
if resp != nil {
defer resp.Body.Close()
}
return err
}

func makePathHelper(namespace, path string) string {
var fullPath string
if namespace != "" {
fullPath = fmt.Sprintf("/v1/%s/sys/mounts/%s", namespace, path)
} else {
fullPath = fmt.Sprintf("/v1/sys/mounts/%s", path)
}
return fullPath
}

func ParseVaultCAConfig(raw map[string]interface{}) (*structs.VaultCAProviderConfig, error) {
config := structs.VaultCAProviderConfig{
CommonCAProviderConfig: defaultCommonConfig(),
Expand Down
12 changes: 7 additions & 5 deletions agent/structs/connect_ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,11 +517,13 @@ type CAConsulProviderState struct {
type VaultCAProviderConfig struct {
CommonCAProviderConfig `mapstructure:",squash"`

Address string
Token string
RootPKIPath string
IntermediatePKIPath string
Namespace string
Address string
Token string
RootPKIPath string
RootPKINamespace string
IntermediatePKIPath string
IntermediatePKINamespace string
Namespace string

CAFile string
CAPath string
Expand Down
6 changes: 6 additions & 0 deletions website/content/docs/connect/ca/vault.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ The configuration options are listed below.
must contain a valid chain, where each certificate is followed by the certificate
that authorized it.

- `RootPKINamespace` / `root_pki_namespace` (`string: <optional>`) - The absolute namespace
that the RootPKIPath is in.

- `IntermediatePKIPath` / `intermediate_pki_path` (`string: <required>`) -
The path to a PKI secrets engine for the generated intermediate certificate.
This certificate will be signed by the configured root PKI path. If this
Expand All @@ -145,6 +148,9 @@ The configuration options are listed below.
When WAN Federation is enabled, every secondary
datacenter must specify a unique `intermediate_pki_path`.

- `IntermediatePKINamespace` / `intermedial_pki_namespace` (`string: <optional>`) - The absolute namespace
that the IntermediatePKIPath is in.

- `CAFile` / `ca_file` (`string: ""`) - Specifies an optional path to the CA
certificate used for Vault communication. If unspecified, this will fallback
to the default system CA bundle, which varies by OS and version.
Expand Down

0 comments on commit 23ef36b

Please sign in to comment.