Skip to content

Commit 3a522d0

Browse files
author
Chris S. Kim
committed
Fix Vault managed intermediate PKI bug
1 parent 6b477ce commit 3a522d0

File tree

2 files changed

+114
-10
lines changed

2 files changed

+114
-10
lines changed

agent/connect/ca/provider_vault.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ type VaultProvider struct {
7070
clusterID string
7171
spiffeID *connect.SpiffeIDSigning
7272
logger hclog.Logger
73+
74+
isConsulMountedIntermediate bool
7375
}
7476

7577
func NewVaultProvider(logger hclog.Logger) *VaultProvider {
@@ -310,9 +312,10 @@ func (v *VaultProvider) GenerateRoot() (RootResult, error) {
310312
},
311313
})
312314
if err != nil {
313-
return RootResult{}, err
315+
return RootResult{}, fmt.Errorf("failed to mount root CA backend")
314316
}
315317

318+
// We want to initialize afterwards
316319
fallthrough
317320
case ErrBackendNotInitialized:
318321
uid, err := connect.CompactUID()
@@ -326,7 +329,7 @@ func (v *VaultProvider) GenerateRoot() (RootResult, error) {
326329
"key_bits": v.config.PrivateKeyBits,
327330
})
328331
if err != nil {
329-
return RootResult{}, err
332+
return RootResult{}, fmt.Errorf("failed to initialize root CA: %w", err)
330333
}
331334
var ok bool
332335
rootPEM, ok = resp.Data["certificate"].(string)
@@ -336,7 +339,7 @@ func (v *VaultProvider) GenerateRoot() (RootResult, error) {
336339

337340
default:
338341
if err != nil {
339-
return RootResult{}, err
342+
return RootResult{}, fmt.Errorf("unexpected error while setting root PKI backend: %w", err)
340343
}
341344
}
342345

@@ -381,19 +384,47 @@ func (v *VaultProvider) setupIntermediatePKIPath() error {
381384
Config: mountConfig,
382385
})
383386
if err != nil {
384-
return err
387+
return fmt.Errorf("failed to mount intermediate PKI backend: %w", err)
385388
}
389+
// Required to determine if we should tune the mount
390+
// if the VaultProvider is ever reconfigured.
391+
v.isConsulMountedIntermediate = true
392+
393+
} else if err == ErrBackendNotInitialized {
394+
// If this is the first time calling setupIntermediatePKIPath, the backend
395+
// will not have been initialized. Since the mount is ready we can suppress
396+
// this error.
386397
} else {
387-
return err
398+
return fmt.Errorf("unexpected error while fetching intermediate CA: %w", err)
388399
}
389400
} else {
390-
err := v.tuneMountNamespaced(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath, &mountConfig)
391-
if err != nil {
392-
v.logger.Warn("Could not update intermediate PKI mount settings", "path", v.config.IntermediatePKIPath, "error", err)
401+
// If Consul was responsible for mounting the intermediate PKI path
402+
// we should update the mount with any new config.
403+
if v.isConsulMountedIntermediate {
404+
// This codepath requires the Vault policy:
405+
//
406+
// path "/sys/mounts/<intermediate_pki_path>/tune" {
407+
// capabilities = [ "update" ]
408+
// }
409+
//
410+
err := v.tuneMountNamespaced(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath, &mountConfig)
411+
if err != nil {
412+
v.logger.Warn("Intermediate PKI path was mounted by Consul but could not be tuned",
413+
"namespace", v.config.IntermediatePKINamespace,
414+
"path", v.config.IntermediatePKIPath,
415+
"error", err,
416+
)
417+
}
418+
419+
} else {
420+
v.logger.Info("Found existing Intermediate PKI path mount",
421+
"namespace", v.config.IntermediatePKINamespace,
422+
"path", v.config.IntermediatePKIPath,
423+
)
393424
}
394425
}
395426

396-
// Create the role for issuing leaf certs if it doesn't exist yet
427+
// Create the role for issuing leaf certs
397428
rolePath := v.config.IntermediatePKIPath + "roles/" + VaultCALeafCertRole
398429
_, err = v.writeNamespaced(v.config.IntermediatePKINamespace, rolePath, map[string]interface{}{
399430
"allow_any_name": true,
@@ -710,7 +741,7 @@ func (v *VaultProvider) SignIntermediate(csr *x509.CertificateRequest) (string,
710741
func (v *VaultProvider) CrossSignCA(cert *x509.Certificate) (string, error) {
711742
rootPEM, err := v.getCA(v.config.RootPKINamespace, v.config.RootPKIPath)
712743
if err != nil {
713-
return "", err
744+
return "", fmt.Errorf("failed to get root CA: %w", err)
714745
}
715746
rootCert, err := connect.ParseCert(rootPEM)
716747
if err != nil {

agent/connect/ca/provider_vault_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,79 @@ func TestVaultCAProvider_GenerateIntermediate(t *testing.T) {
924924
require.NotEqual(t, orig, new)
925925
}
926926

927+
func TestVaultCAProvider_VaultManaged(t *testing.T) {
928+
const vaultManagedPKIPolicy = `
929+
path "/pki-root/" {
930+
capabilities = [ "read" ]
931+
}
932+
933+
path "/pki-root/root/sign-intermediate" {
934+
capabilities = [ "update" ]
935+
}
936+
937+
path "/pki-intermediate/*" {
938+
capabilities = [ "create", "read", "update", "delete", "list" ]
939+
}
940+
941+
path "auth/token/renew-self" {
942+
capabilities = [ "update" ]
943+
}
944+
945+
path "auth/token/lookup-self" {
946+
capabilities = [ "read" ]
947+
}
948+
`
949+
950+
testVault, err := runTestVault(t)
951+
if err != nil {
952+
t.Fatalf("err: %v", err)
953+
}
954+
955+
testVault.WaitUntilReady(t)
956+
957+
client := testVault.Client()
958+
959+
client.SetToken("root")
960+
961+
// Mount pki root externally
962+
require.NoError(t, client.Sys().Mount("pki-root", &vaultapi.MountInput{
963+
Type: "pki",
964+
Description: "root CA backend for Consul Connect",
965+
Config: vaultapi.MountConfigInput{
966+
MaxLeaseTTL: "12m",
967+
},
968+
}))
969+
_, err = client.Logical().Write("pki-root/root/generate/internal", map[string]interface{}{
970+
"common_name": "testconsul",
971+
})
972+
require.NoError(t, err)
973+
974+
// Mount pki intermediate externally
975+
require.NoError(t, client.Sys().Mount("pki-intermediate", &vaultapi.MountInput{
976+
Type: "pki",
977+
Description: "intermediate CA backend for Consul Connect",
978+
Config: vaultapi.MountConfigInput{
979+
MaxLeaseTTL: "6m",
980+
},
981+
}))
982+
983+
// Generate a policy and token for the VaultProvider to use
984+
require.NoError(t, client.Sys().PutPolicy("consul-ca", vaultManagedPKIPolicy))
985+
tcr := &vaultapi.TokenCreateRequest{
986+
Policies: []string{"consul-ca"},
987+
}
988+
secret, err := testVault.client.Auth().Token().Create(tcr)
989+
require.NoError(t, err)
990+
providerToken := secret.Auth.ClientToken
991+
992+
// We want to test the provider.Configure() step
993+
_, err = createVaultProvider(t, true, testVault.Addr, providerToken, nil)
994+
if err != nil {
995+
testVault.Stop()
996+
t.Fatalf("err: %v", err)
997+
}
998+
}
999+
9271000
func getIntermediateCertTTL(t *testing.T, caConf *structs.CAConfiguration) time.Duration {
9281001
t.Helper()
9291002

0 commit comments

Comments
 (0)