Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Feature Design for: Key Management Service (KMS) with HSM Integration

Project Introduction

This document outlines the design for a comprehensive Key Management Service (KMS) in Apache CloudStack that provides envelope encryption for volume encryption using Hardware Security Modules (HSMs) or a database-backed fallback provider (also used for testing).

...

KEK Size vs DEK Size (Important Distinction)

WhatSourceUsed For
KEK sizekeybits parameter in createKMSKey / rotateKMSKey (stored in kms_keys.key_bits)Size of the Key Encryption Key stored in the HSM or database. Used when creating or rotating a KEK.
DEK sizeGlobal config kms.dek.size.bitsSize of the Data Encryption Key generated per volume. Used in generateVolumeKeyWithKek().
  • The KMS key's keybits controls only the KEK size (e.g., 256-bit AES key in the HSM).
  • The global setting kms.dek.size.bits controls the DEK size for all new encrypted volumes.
  • DEK size is independent of the KMS key; all volumes get DEKs of the configured global size.

...

  • HSM Profile Management (Admin only)

    • Add HSM Profile: Configure connection to HSM devices (PKCS#11-compliant)
    • Profile Scoping: User-owned, zone-level, or global (systempublic) profiles
    • Profile Validation: Health check on configured HSM connections
    • Sensitive data encryption: PINs and passwords are encrypted via DBEncryptionUtil before storage
  • KMS Key Management

    • Key Creation: Create KMS keys (KEKs) bound to an HSM profile, zone, and account
    • Key Update: Enable/disable keys, update name and description
    • Key Deletion: Soft-delete keys (only if not in use by volumes or wrapped keys)
    • Key Listing: List keys with filtering by purpose, zone, state, and account
  • Key Rotation

    • Same-HSM Rotation: Create a new KEK version in the same HSM with a new label
    • Cross-HSM Migration: Create a new KEK version in a different HSM profile
    • Transaction Atomicity: Database updates for new KEK version and key profile are wrapped in a Transaction.execute() block; orphaned HSM keys are cleaned up on DB failure
    • Background Rewrap: Gradual re-encryption of wrapped keys in configurable batches
  • Volume Encryption Integration

    • DEK Generation: Generate random DEKs (configurable size via kms.dek.size.bits) and wrap them with the active KEK version
    • DEK Unwrapping: Unwrap DEKs on demand for volume access (plaintext DEKs are zeroized after use)
    • Volume Migration: Migrate legacy passphrase-encrypted volumes to KMS encryption
  • Plugin Architecture

    • DatabaseKMSProvider: Database-backed KEK storage with AES/GCM/NoPadding encryption via DBEncryptionUtil
    • PKCS11HSMProvider: PKCS#11 HSM integration with per-profile session pooling and AES/CBC/PKCS5Padding wrapping
  • Concurrency & Cluster Safety

    • Bounded thread pool for KMS operations: ThreadPoolExecutor(core=2, max=100, keepAlive=60s, SynchronousQueue) with daemon threads
    • Cluster-aware rewrap: GlobalLock("kms.rewrap.worker") prevents duplicate rewrap work across management server nodes
    • ScheduledExecutorService (replaces java.util.Timer) for robust periodic rewrap scheduling

...

  1. User-Owned Profile: account_id set → visible only to that account
  2. Zone Admin Profile: zone_id set, account_id NULL → visible to all accounts in that zone
  3. Global Admin Profile: zone_id NULL, account_id NULL, system is_public = TRUE → visible to all accounts in all zones

...

  • Authorization: Admin, ResourceAdmin, DomainAdmin, User
  • Async: No

Parameters:

ParameterRequiredTypeDescription
nameYesStringName of the KMS key
descriptionNoStringDescription of the KMS key
purposeYesStringPurpose of the key (volume, tls)
zoneidYesUUIDZone ID where the key will be valid
hsmprofileidYesUUIDHSM profile ID to create the KEK in
keybitsNoIntegerKEK size in bits (128, 192, 256). Default: 256
accountNoStringAccount name (admin use)
domainidNoUUIDDomain ID (admin use)

CloudMonkey Example:

cmk createKMSKey \
  name="volume-encryption-key" \
  description="Production volume encryption" \
  purpose="volume" \
  keybits=256 \
  zoneid=<zone-uuid> \
  hsmprofileid=<hsm-profile-uuid>

listKMSKeys

Lists KMS keys available to the caller.

  • Authorization: Admin, ResourceAdmin, DomainAdmin, User

Parameters:


listKMSKeys

Lists KMS keys available to the caller.

  • Authorization: Admin, ResourceAdmin, DomainAdmin, User

Parameters:

Filter by
ParameterRequiredTypeDescription
idNoUUIDList KMS key by UUID
purposeNoStringFilter by purpose
zoneidNoUUIDFilter by
ParameterRequiredTypeDescription
idNoUUIDList KMS key by UUID
purposeNoStringFilter by purpose
zoneidNoUUID
zone
stateNoStringFilter by state (Enabled, Disabled)

...

cmk listKMSKeys purpose=volume state=Enabled

updateKMSKey

Updates KMS key name, description, or state.

  • Authorization: Admin, ResourceAdmin, DomainAdmin, User
  • Async: Yes

Parameters:

ParameterRequiredTypeDescription
idYesUUIDKMS key UUID
nameNoStringNew name
descriptionNoStringNew description
enabledNoBooleanEnable/disable the key

CloudMonkey Example:

cmk updateKMSKey id=<kms-key-uuid> enabled=false

deleteKMSKey

deleteKMSKey

Deletes a KMS key (Deletes a KMS key (only if not referenced by volumes or wrapped keys).

  • Authorization: Admin, ResourceAdmin, DomainAdmin, User
  • Async: Yes

Parameters:

ParameterRequiredTypeDescription
idYesUUIDKMS key UUID

CloudMonkey Example:

...

rotateKMSKey

Rotates KEK by creating a new version and scheduling gradual re-encryption of wrapped keys.

  • Authorization: Admin only
  • Async: Yes

Parameters:

ParameterRequiredTypeDescription
idYesUUIDKMS key UUID to rotate
keybitsNoIntegerKey size for new KEK (default: same as current)
hsmprofileidNoUUIDTarget HSM profile for cross-HSM migration

CloudMonkey Examples:

# Same-HSM rotation
cmk rotateKMSKey id=<kms-key-uuid>

# Cross-HSM migration
cmk rotateKMSKey id=<kms-key-uuid> hsmprofileid=<target-hsm-profile-uuid>

migrateVolumesToKMS

Migrates passphrase-based volumes to KMS encryption.

  • Authorization: Admin only
  • Async: Yes

Parameters:

migrateVolumesToKMS

Migrates passphrase-based volumes to KMS encryption.

  • Authorization: Admin only
  • Async: Yes

Parameters:

ParameterRequiredTypeDescription
zoneidYesUUIDZone ID
idYesUUIDKMS key ID to migrate volumes to
accountNoStringMigrate volumes for specific account
domainidNoUUIDDomain ID

CloudMonkey Example:

cmk migrateVolumesToKMS zoneid=<zone-uuid> id=<kms-key-uuid>

HSM Profile APIs

addHSMProfile

HSM Profile APIs

addHSMProfile

Adds a new HSM profile for Adds a new HSM profile for connecting to an HSM device.

  • Authorization: Admin only
  • Request has sensitive info: Yes (PIN, passwords)

Parameters:

systemSystem
ParameterRequiredTypeDescription
nameYesStringHSM profile name
protocolNoStringProtocol (PKCS11, KMIP, etc.). Default: pkcs11
zoneidNoUUIDZone ID (null = global scope)
domainidNoUUIDDomain ID
accountNoStringAccount name
is_publicNoBoolean
Public profile (globally available, root admin only)
vendornameNoStringHSM vendor name
detailsNoMapHSM configuration details

PKCS#11 details keys: library (path to PKCS#11 library), slot (slot number), pin (HSM PIN, encrypted at rest), token_label (token label), minSessions, maxSessions

CloudMonkey Example:

...

slot number), pin (HSM PIN, encrypted at rest), token_label (token label), minSessions, maxSessions


listHSMProfiles

Lists HSM profiles visible to the caller.

  • Authorization: Admin, ResourceAdmin, DomainAdmin, User
  • Response has sensitive info: Yes (encrypted values shown as ENC(...))

Parameters:

ParameterRequiredTypeDescription
idNoUUIDHSM profile ID
zoneidNoUUIDZone ID
protocolNoStringProtocol filter
enabledNoBoolean
Enabled filter

CloudMonkey Example:

...

Enabled filter

updateHSMProfile

Updates an HSM profile name or enabled state.

  • Authorization: Admin only

Parameters:

ParameterRequiredTypeDescription
idYesUUIDHSM profile UUID
nameNoStringNew name
enabledNoBooleanEnable/disable

Note: Updating configuration details is not currently supported. To change PKCS#11 parameters (e.g., PIN), delete and re-create the HSM profile.

CloudMonkey Example:

cmk updateHSMProfile id=<profile-uuid> enabled=false

deleteHSMProfile

Deletes an HSM profile (only if not in use by any KEK versions).

  • Authorization: Admin only

Parameters:

ParameterRequiredTypeDescription
idYesUUIDHSM profile UUID

CloudMonkey Example:

cmk deleteHSMProfile id=<profile-uuid>

Global Settings

Setting KeyScopeTypeDefaultDescription
kms.dek.size.bitsGlobalInteger256Size of DEKs in bits for new volumes (128, 192, 256)
kms.retry.countGlobalInteger3Number of retry attempts for transient KMS failures
kms.retry.delay.msGlobalInteger1000Delay in milliseconds between retry attempts
kms.operation.timeout.secGlobalInteger30Per-attempt timeout for KMS operations
kms.rewrap.batch.sizeGlobalInteger50Wrapped keys rewrapped per batch in background job
kms.rewrap.interval.msGlobalLong300000Interval between background rewrap executions (5 min)

Database Changes

New Tables

...

Code Block
CREATE TABLE IF NOT EXISTS `cloud`.`kms_hsm_profiles` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `uuid` VARCHAR(40) NOT NULL,
    `name` VARCHAR(255) NOT NULL,
    `protocol` VARCHAR(32) NOT NULL COMMENT 'PKCS11, KMIP, AWS_KMS, etc.',
    `account_id` BIGINT UNSIGNED COMMENT 'null = admin-provided',
    `domain_id` BIGINT UNSIGNED,
    `zone_id` BIGINT UNSIGNED COMMENT 'null = global scope',
    `vendor_name` VARCHAR(64),
    `enabled` BOOLEAN NOT NULL DEFAULT TRUE,
    `system``is_public` BOOLEAN NOT NULL DEFAULT FALSE,
    `created` DATETIME NOT NULL,
    `removed` DATETIME,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_uuid` (`uuid`),
    UNIQUE KEY `uk_account_name` (`account_id`, `name`, `removed`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

...