Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Multiple Issuers in PKI Secret Engine Mounts - PKI Pod #15277

Merged
merged 76 commits into from May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
18fcfb2
Starter PKI CA Storage API (#14796)
stevendpclark Mar 31, 2022
d57be55
Handle resolving key, issuer references
cipherboy Mar 31, 2022
b8fe80d
Add method to fetch an issuer's cert bundle
cipherboy Mar 31, 2022
55f0841
Refactor certutil PrivateKey PEM handling
cipherboy Apr 1, 2022
ca3e48d
Add importKey, importCert to PKI storage
cipherboy Apr 5, 2022
a93690f
Add tests for importing issuers, keys
cipherboy Apr 5, 2022
1411be7
Implement PKI storage migration.
stevendpclark Mar 31, 2022
62467e0
Make fetchCAInfo aware of new storage layout
cipherboy Mar 31, 2022
c0c11c9
Begin /issuers API endpoints
cipherboy Apr 1, 2022
5a4c6ea
Add import to PKI Issuers API
cipherboy Apr 1, 2022
cfe2bc8
Add /issuer/:ref/sign-intermediate endpoint
cipherboy Apr 5, 2022
1c589eb
Add /issuer/:ref/sign-self-issued endpoint
cipherboy Apr 5, 2022
2de0e9c
Add /issuer/:ref/sign-verbatim endpoint
cipherboy Apr 5, 2022
51ebbbd
Allow configuration of default issuers
cipherboy Apr 6, 2022
81498a7
Fix fetching default issuers
cipherboy Apr 6, 2022
b35f2e6
Add /issuer/:ref/{sign,issue}/:role
cipherboy Apr 6, 2022
3750055
Add support root issuer generation
stevendpclark Apr 8, 2022
95c10b8
Add support for issuer generate intermediate end-point
stevendpclark Apr 8, 2022
5cd5bad
Update issuer and key arguments to consistent values
stevendpclark Apr 11, 2022
6ddd258
Add utility methods to fetch common ref and name arguments
stevendpclark Apr 11, 2022
acebaea
Rename common PKI backend handlers
stevendpclark Apr 11, 2022
9f6731c
Move setting PKI defaults from writeCaBundle to proper import{keys,is…
stevendpclark Apr 11, 2022
5950270
Introduce constants for issuer_ref, rename isKeyDefaultSet...
stevendpclark Apr 11, 2022
a926452
Fix legacy PKI sign-verbatim api path
stevendpclark Apr 11, 2022
6511333
Use import code to handle intermediate, config/ca
cipherboy Apr 11, 2022
0a30e50
Clarify error message on missing defaults
cipherboy Apr 11, 2022
bfd41cf
Update test semantics for new changes
cipherboy Apr 11, 2022
547012f
Add support for deleting all keys, issuers
cipherboy Apr 12, 2022
f34f024
Introduce defaultRef constant within PKI
stevendpclark Apr 12, 2022
705d64f
Rework PKI test TestBackend_Root_Idempotency
stevendpclark Apr 12, 2022
12131b3
Assign Name=current to migrated key and issuer
stevendpclark Apr 13, 2022
1546015
Build CRL upon PKI intermediary set-signed api called
stevendpclark Apr 13, 2022
d9c7f2a
Identify which certificate or key failed
cipherboy Apr 15, 2022
fe33037
PKI migration writes out empty migration log entry
stevendpclark Apr 19, 2022
a25845e
Add chain-building logic to PKI issuers path
cipherboy Apr 12, 2022
e7d5258
Return CA Chain when fetching issuers
cipherboy Apr 13, 2022
ade2b49
Add testing for chain building
cipherboy Apr 13, 2022
4caf0a2
Allow manual construction of issuer chain
cipherboy Apr 15, 2022
5ffcf4b
Fix handling of duplicate names
cipherboy Apr 18, 2022
4a3de6a
Add tests for manual chain building
cipherboy Apr 18, 2022
361e8b8
Add stricter verification of issuers PEM format
cipherboy Apr 19, 2022
3768fde
Fix full chain building
cipherboy Apr 12, 2022
3ff4dfd
Add stricter tests for full chain construction
cipherboy Apr 12, 2022
46a2e48
Rename PKI types to avoid constant variable name collisions
stevendpclark Apr 20, 2022
c4d384f
Update CRL handling for multiple issuers
cipherboy Apr 21, 2022
a63f803
Allow fetching updated CRL locations
cipherboy Apr 21, 2022
05c44f8
Remove legacy CRL storage location test case
cipherboy Apr 21, 2022
1182a71
Update to CRLv2 Format to copy RawIssuer
kitography Apr 21, 2022
48ec19e
Add comment regarding CRL non-assignment in GOTO
cipherboy Apr 21, 2022
9189e7f
Allow fetching the specified issuer's CRL
cipherboy Apr 22, 2022
9f10a67
Add new PKI key prefix to seal wrapped storage (#15126)
stevendpclark Apr 22, 2022
2b740ec
Refactor common backend initialization within backend_test
stevendpclark Apr 13, 2022
8145882
Add ability to read legacy cert bundle if the migration has not occur…
stevendpclark Apr 13, 2022
e9e4a06
Always write migration entry to trigger secondary clusters to wake up
stevendpclark Apr 22, 2022
5b692bb
Update CA Chain to report entire chain
cipherboy Apr 25, 2022
21e0d48
Allow explicit issuer override on roles
cipherboy Apr 25, 2022
104b42e
Add tests for role-based issuer selection
cipherboy Apr 25, 2022
8b2f2b0
Expand NotAfter limit enforcement behavior
cipherboy Apr 25, 2022
8aa7caa
Add tests for expanded issuance behaviors
cipherboy Apr 25, 2022
35a8716
Add warning on keyless default issuer (#15178)
cipherboy Apr 26, 2022
1b53ccd
Update PKI to new Operations framework (#15180)
cipherboy Apr 26, 2022
c57c1ab
Kitography/vault 5474 rebase (#15150)
kitography Apr 28, 2022
e9abf67
Add alternative proposal PKI aliased paths (#15211)
cipherboy Apr 29, 2022
06aed80
Clean up various warnings within the PKI package (#15230)
stevendpclark Apr 29, 2022
52087ad
Rebuild CRLs on secondary performance clusters post migration and on …
stevendpclark Apr 25, 2022
db40514
Schedule rebuilding PKI CRLs on active nodes only
stevendpclark Apr 27, 2022
9157497
Return legacy CRL storage path when no migration has occurred.
stevendpclark Apr 28, 2022
94169e5
Handle issuer, keys locking (#15227)
cipherboy Apr 29, 2022
358e1b8
Address PKI to properly support managed keys (#15256)
stevendpclark May 2, 2022
6ac18d1
Correctly handle rebuilding remaining chains
cipherboy Apr 28, 2022
39b8093
Remove legacy CRL bundle on world deletion
cipherboy Apr 28, 2022
2b4d256
Remove deleted issuers' CRL entries
cipherboy Apr 28, 2022
f82d3ef
Add unauthed JSON fetching of CRLs, Issuers (#15253)
cipherboy May 2, 2022
725bd2e
Add unauthenticated issuers endpoints to PKI table
cipherboy May 2, 2022
15bc598
Add issuer usage restrictions bitset
cipherboy May 3, 2022
bba4bcb
PKI Pod rotation Add Base Changelog (#15283)
kitography May 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
152 changes: 139 additions & 13 deletions builtin/logical/pki/backend.go
Expand Up @@ -5,8 +5,11 @@ import (
"fmt"
"strings"
"sync"
"sync/atomic"
"time"

"github.com/hashicorp/vault/sdk/helper/consts"

"github.com/armon/go-metrics"
"github.com/hashicorp/vault/helper/metricsutil"
"github.com/hashicorp/vault/helper/namespace"
Expand All @@ -22,22 +25,27 @@ const (

/*
* PKI requests are a bit special to keep up with the various failure and load issues.
* The main ca and intermediate requests are always forwarded to the Primary cluster's active
* node to write and send the key material/config globally across all clusters.
*
* CRL/Revocation and Issued certificate apis are handled by the active node within the cluster
* they originate. Which means if a request comes into a performance secondary cluster the writes
* Any requests to write/delete shared data (such as roles, issuers, keys, and configuration)
* are always forwarded to the Primary cluster's active node to write and send the key
* material/config globally across all clusters. Reads should be handled locally, to give a
* sense of where this cluster's replication state is at.
*
* CRL/Revocation and Fetch Certificate APIs are handled by the active node within the cluster
* they originate. This means, if a request comes into a performance secondary cluster, the writes
* will be forwarded to that cluster's active node and not go all the way up to the performance primary's
* active node.
*
* If a certificate issue request has a role in which no_store is set to true that node itself
* will issue the certificate and not forward the request to the active node.
* If a certificate issue request has a role in which no_store is set to true, that node itself
* will issue the certificate and not forward the request to the active node, as this does not
* need to write to storage.
*
* Following the same pattern if a managed key is involved to sign an issued certificate request
* Following the same pattern, if a managed key is involved to sign an issued certificate request
* and the local node does not have access for some reason to it, the request will be forwarded to
* the active node within the cluster only.
*
* To make sense of what goes where the following bits need to be analyzed within the codebase.
*
* 1. The backend LocalStorage paths determine what storage paths will remain within a
* cluster and not be forwarded to a performance primary
* 2. Within each path's OperationHandler definition, check to see if ForwardPerformanceStandby &
Expand Down Expand Up @@ -69,11 +77,19 @@ func Backend(conf *logical.BackendConfig) *backend {
"ca",
"crl/pem",
"crl",
"issuer/+/crl/der",
"issuer/+/crl/pem",
"issuer/+/crl",
"issuer/+/pem",
"issuer/+/der",
"issuer/+/json",
"issuers",
},

LocalStorage: []string{
"revoked/",
"crl",
legacyCRLPath,
"crls/",
"certs/",
},

Expand All @@ -83,7 +99,8 @@ func Backend(conf *logical.BackendConfig) *backend {
},

SealWrapStorage: []string{
"config/ca_bundle",
legacyCertBundlePath,
keyPrefix,
},
},

Expand All @@ -103,43 +120,83 @@ func Backend(conf *logical.BackendConfig) *backend {
pathSign(&b),
pathIssue(&b),
pathRotateCRL(&b),
pathRevoke(&b),
pathTidy(&b),
pathTidyStatus(&b),

// Issuer APIs
pathListIssuers(&b),
pathGetIssuer(&b),
pathGetIssuerCRL(&b),
pathImportIssuer(&b),
pathIssuerIssue(&b),
pathIssuerSign(&b),
pathIssuerSignIntermediate(&b),
pathIssuerSignSelfIssued(&b),
pathIssuerSignVerbatim(&b),
pathIssuerGenerateRoot(&b),
pathRotateRoot(&b),
pathIssuerGenerateIntermediate(&b),
pathCrossSignIntermediate(&b),
pathConfigIssuers(&b),
pathReplaceRoot(&b),

// Key APIs
pathListKeys(&b),
pathKey(&b),
pathGenerateKey(&b),
pathImportKey(&b),
pathConfigKeys(&b),

// Fetch APIs have been lowered to favor the newer issuer API endpoints
pathFetchCA(&b),
pathFetchCAChain(&b),
pathFetchCRL(&b),
pathFetchCRLViaCertPath(&b),
pathFetchValidRaw(&b),
pathFetchValid(&b),
pathFetchListCerts(&b),
pathRevoke(&b),
pathTidy(&b),
pathTidyStatus(&b),
},

Secrets: []*framework.Secret{
secretCerts(&b),
},

BackendType: logical.TypeLogical,
BackendType: logical.TypeLogical,
InitializeFunc: b.initialize,
Invalidate: b.invalidate,
PeriodicFunc: b.periodicFunc,
}

b.crlLifetime = time.Hour * 72
b.tidyCASGuard = new(uint32)
b.tidyStatus = &tidyStatus{state: tidyStatusInactive}
b.storage = conf.StorageView
b.backendUuid = conf.BackendUUID

b.pkiStorageVersion.Store(0)

b.crlBuilder = &crlBuilder{}
return &b
}

type backend struct {
*framework.Backend

backendUuid string
storage logical.Storage
crlLifetime time.Duration
revokeStorageLock sync.RWMutex
tidyCASGuard *uint32

tidyStatusLock sync.RWMutex
tidyStatus *tidyStatus

pkiStorageVersion atomic.Value
crlBuilder *crlBuilder

// Write lock around issuers and keys.
issuersLock sync.RWMutex
}

type (
Expand Down Expand Up @@ -233,3 +290,72 @@ func (b *backend) metricsWrap(callType string, roleMode int, ofunc roleOperation
return resp, err
}
}

// initialize is used to perform a possible PKI storage migration if needed
func (b *backend) initialize(ctx context.Context, _ *logical.InitializationRequest) error {
// Load up our current pki storage state, no matter the host type we are on.
b.updatePkiStorageVersion(ctx)

// Early exit if not a primary cluster or performance secondary with a local mount.
if b.System().ReplicationState().HasState(consts.ReplicationDRSecondary|consts.ReplicationPerformanceStandby) ||
(!b.System().LocalMount() && b.System().ReplicationState().HasState(consts.ReplicationPerformanceSecondary)) {
b.Logger().Debug("skipping PKI migration as we are not on primary or secondary with a local mount")
return nil
}

b.issuersLock.Lock()
defer b.issuersLock.Unlock()

if err := migrateStorage(ctx, b, b.storage); err != nil {
b.Logger().Error("Error during migration of PKI mount: " + err.Error())
return err
}

b.updatePkiStorageVersion(ctx)

return nil
}

func (b *backend) useLegacyBundleCaStorage() bool {
version := b.pkiStorageVersion.Load()
return version == nil || version == 0
}

func (b *backend) updatePkiStorageVersion(ctx context.Context) {
info, err := getMigrationInfo(ctx, b.storage)
if err != nil {
b.Logger().Error(fmt.Sprintf("Failed loading PKI migration status, staying in legacy mode: %v", err))
return
}

if info.isRequired {
b.Logger().Info("PKI migration is required, reading cert bundle from legacy ca location")
b.pkiStorageVersion.Store(0)
} else {
b.Logger().Debug("PKI migration completed, reading cert bundle from key/issuer storage")
sgmiller marked this conversation as resolved.
Show resolved Hide resolved
b.pkiStorageVersion.Store(1)
}
}

func (b *backend) invalidate(ctx context.Context, key string) {
switch {
case strings.HasPrefix(key, legacyMigrationBundleLogKey):
// This is for a secondary cluster to pick up that the migration has completed
// and reset its compatibility mode and rebuild the CRL locally.
b.updatePkiStorageVersion(ctx)
b.crlBuilder.requestRebuildIfActiveNode(b)
case strings.HasPrefix(key, issuerPrefix):
// If an issuer has changed on the primary, we need to schedule an update of our CRL,
// the primary cluster would have done it already, but the CRL is cluster specific so
// force a rebuild of ours.
if !b.useLegacyBundleCaStorage() {
b.crlBuilder.requestRebuildIfActiveNode(b)
} else {
b.Logger().Debug("Ignoring invalidation updates for issuer as the PKI migration has yet to complete.")
}
}
}

func (b *backend) periodicFunc(ctx context.Context, request *logical.Request) error {
return b.crlBuilder.rebuildIfForced(ctx, b, request)
}