Repository: hashicorp/nomad
File: nomad/encrypter.go (Go)

Code before:

	return claims, nil
}

// AddUnwrappedKey stores the key in the keystore and creates a new cipher for
// it. This is called in the RPC handlers on the leader and from the legacy
// KeyringReplicator.
func (e *Encrypter) AddUnwrappedKey(rootKey *structs.UnwrappedRootKey, isUpgraded bool) (*structs.RootKey, error) {

	// note: we don't lock the keyring here but inside addCipher
	// instead, so that we're not holding the lock while performing
	// local disk writes
	if err := e.addCipher(rootKey); err != nil {
		return nil, err
	}
	return e.wrapRootKey(rootKey, isUpgraded)
}

// AddWrappedKey creates decryption tasks for keys we've previously stored in
// Raft. It's only called as a goroutine by the FSM Apply for WrappedRootKeys,
// but it returns an error for ease of testing.
func (e *Encrypter) AddWrappedKey(ctx context.Context, wrappedKeys *structs.RootKey) error {

	// If the passed root key does not contain any wrapped keys, it has no
	// decryption tasks to perform and therefore should not supersede or impact
	// tasks running for the same keyID.
	if len(wrappedKeys.WrappedKeys) == 0 {
		return nil
	}

	logger := e.log.With("key_id", wrappedKeys.KeyID)

	e.lock.Lock()

	_, err := e.cipherSetByIDLocked(wrappedKeys.KeyID)
	if err == nil {

		// key material for each key ID is immutable so nothing to do, but we
		// can cancel and remove any running decrypt tasks
		if cancel, ok := e.decryptTasks[wrappedKeys.KeyID]; ok {
			cancel()
			delete(e.decryptTasks, wrappedKeys.KeyID)
		}
		e.lock.Unlock()
		return nil
	}

	if cancel, ok := e.decryptTasks[wrappedKeys.KeyID]; ok {
		// stop any previous tasks for this same key ID under the assumption
		// they're broken or being superseded, but don't remove the CancelFunc
		// from the map yet so that other callers don't think we can continue

Review comment (suggestion):
I might add a note here about the limited circumstances we expect this to be true: when FSM applying / restoring from snapshot the metadata for a legacy key. When we remove backcompat for legacy keys entirely (1.12.0), we can potentially remove or at least review this again.

Code after:

	return claims, nil
}

// AddUnwrappedKey stores the key in the keystore and creates a new cipher for
// it. This is called in the RPC handlers on the leader and from the legacy
// KeyringReplicator.
func (e *Encrypter) AddUnwrappedKey(rootKey *structs.UnwrappedRootKey, isUpgraded bool) (*structs.RootKey, error) {

	// note: we don't lock the keyring here but inside addCipher
	// instead, so that we're not holding the lock while performing
	// local disk writes
	if err := e.addCipher(rootKey); err != nil {
		return nil, err
	}
	return e.wrapRootKey(rootKey, isUpgraded)
}

// AddWrappedKey creates decryption tasks for keys we've previously stored in
// Raft. It's only called as a goroutine by the FSM Apply for WrappedRootKeys,
// but it returns an error for ease of testing.
func (e *Encrypter) AddWrappedKey(ctx context.Context, wrappedKeys *structs.RootKey) error {

	// If the passed root key does not contain any wrapped keys, it has no
	// decryption tasks to perform and therefore should not supersede or impact
	// tasks running for the same keyID.
	//
	// This conditional will only be hit when the FSM is taking action on
	// fsm.RootKeyMetaSnapshot and structs.RootKeyMetaUpsertRequestType types
	// which represent legacy style keys. When support is removed for these in
	// 1.12.0, we should reconsider this conditional too.
	if len(wrappedKeys.WrappedKeys) == 0 {
		return nil
	}

	logger := e.log.With("key_id", wrappedKeys.KeyID)

	e.lock.Lock()

	_, err := e.cipherSetByIDLocked(wrappedKeys.KeyID)
	if err == nil {

		// key material for each key ID is immutable so nothing to do, but we
		// can cancel and remove any running decrypt tasks
		if cancel, ok := e.decryptTasks[wrappedKeys.KeyID]; ok {
			cancel()
			delete(e.decryptTasks, wrappedKeys.KeyID)
		}
		e.lock.Unlock()
		return nil
	}