Connect with STACKIT-KMS
The STACKIT-KMS is basically some kind of network-HMS. User can upload and manage keys there, which can be used as key-encrypteion-keys (kek) by other services. In order to provide Openstack the ability to use this STACKIT-KMS as crypto-backend for creating encrypted volumes, Barbican and Cinder were enhanced to be able to work with the KMS. The primary key, which is used to encrypt and decrypt the Cinder volume, is still stored inside the database of Barbican, but within Barbican this key is encrypted against a user-defined key within the STACKIT-KMS. The key in the STACKID-KMS is identified by various IDs and linked by a service-account.
Requirements
In openstack there is an encrypted volume type necessary. Every new Volume, which should be an encrypted volume, requires this encrypted volume type while creation. The following example-command creates such a volume type named LUKS
. The parameters like cipher, key-size and name can be replaced by other values, if necessary.
openstack volume type create \
--encryption-provider nova.volume.encryptors.luks.LuksEncryptor \
--encryption-cipher aes-xts-plain64 \
--encryption-key-size 256 \
--encryption-control-location front-end \
LUKS
Configurations
In openstack the new feature is not used per default, because it can not be used without a working STACKIT-KMS, so it has to be explicitly within the openstack configs.
Cinder
In Cinder the usage of secret-consumer has to be enabled by setting use_secret_consumer
inside the key_manager
-section to true
. If this option is not set, it is false
per default.
key_manager:
backend: barbican
use_secret_consumer: true
This option prevents Cinder from cloning Barbican-secrets. This is absolute necessary when using the STACKIT-KMS as backend. When using the STACKIT-KMS, there are a bunch of metadata in the Barbican-database stored, which never leaving Barbican again and so they can not be cloned by Cinder, when Cinder try to clone the Barbican-secret. If this option is not set, an error will be thrown by Cinder when an encrypted volume, while creating a backup of a volume from a snapshot of an encrypted volume. Creating a new encrypted volume would still work, even if the flag was not set in the config.
The option should work with other crypto backends like simple-crypto as well, but was not tested, because it would have to be tested with all available backends, which is not possible at the moment. So the secret-consumer were made optional.
Barbican
Barbican got a new plugin, which can communicate with the STACKIT-KMS as backend. This stackit_kms
plugin has to be enabled in the Barbican-config.
crypto:
enabled_crypto_plugins:
- stackit_kms
Additionally the plugin requires a bunch of configurations to be able to access the STACKIT-KMS over the service-account.
The following shows an example for the configuration:
stackit_kms_plugin:
plugin_name: "STACKIT KMS"
# kms connection
kms_url: "https://kms.api.eu01.qa.stackit.cloud"
kms_endpoint_version: v1beta
# service-account connection
sa_url: "https://service-account.api.stg.stackit.cloud"
sa_endpoint_version: v2
# idp connection
idp_url: "https://accounts.qa.stackit.cloud/oauth"
idp_endpoint_version: v2
# account for impersonation
client_id: $CLIENT_ID
client_secret: $CLIENT_SECRET
client_id
: predefined account, which is allowed and configured to impersonate any service-account. Such an account is for example already used by the STACKIT-Metadata-API.idp_url
: the IDP-endpoint, where the client can login and get an access-token.sa_url
: endpoint to access the service-accounts and impersonate these service-accounts. Which the Access-Token from the IDP-endpoint and new impersonated token is requested here.kms_url
: the address of the STACKIT-KMS. The impersonated token from the service-account is used here to get indirect access to the keys of the user, to encrypt and decrypt the tokens stored in Barbican.
In case the fields are missing or invalid or the client has no impersonation permissions, trying to store secrets in Barbican will fail with an internal error-message and only print error-information in the logs of Barbican.
Usage
create Barbican-secret
Basically storing a secret in Barbican requires the following information:
Project-ID
Region
Service-Account
Keyring-ID
Key-ID
Key-Version
Payload
Project-ID
and Region
are necessary to access the STACKIT-KMS endpoint. The service-account
is the account linked to the key. Keyring-ID
, Key-ID
and Key-Version
the used to identify the key within the account of the user within the STACKIT-KMS. The Payload
is the key, which will be encrypted by the key in the STACKIT-KMS and stored within the database of Barbican. This is also at the end the key, which will be attached to the encrypted volume in Openstack.
Creating a secret in Barbican with STACKIT-KMS backend it not very convenient. It was the goal to provide the feature only with a plugin and without any changes at the API. Unfortunately the API of Barbican is very limited and doesn’t provide an input for additional key metadata. As workaround the secret payload is used to insert the additional information. The payload when storing a secret in barbican with the STACKIT-KMS-plugin is required to be a json-string, which contains also the metadata beside the real secret-payload. This json looks like this:
{
"stackit_kms_protject_id": "467803a8-9925-4fcb-9225-78ef0474711f",
"stackit_kms_region": "eu01",
"stackit_service_account": "kms-operator-ka4i9h1@sa.stackit.cloud",
"stackit_kms_keyring_id": "b5eab460-4aa1-4de8-821c-06c7ba12e4fc",
"stackit_kms_key_id": "69cb35ff-ff64-4b44-8af2-0fcd133af583",
"stackit_kms_key_version": "1",
"data":"dGVzdC1zZWNyZXQ="
}
All keys are mandatory. The keys which start with stackit_kms_
are the connection information to link and identify the key within STACKIT-KMS. data
is the secret-payload. This data has to be base64-encrypted.
To be able to give this into barbican, the whole json-string has to be converted into base64 too. For the provided example-json, this would look like this
eyJzdGFja2l0X2ttc19wcm90amVjdF9pZCI6ICI0Njc4MDNhOC05OTI1LTRmY2ItOTIyNS03OGVmMDQ3NDcxMWYiLAoic3RhY2tpdF9rbXNfcmVnaW9uIjogImV1MDEiLAoic3RhY2tpdF9zZXJ2aWNlX2FjY291bnQiOiAia21zLW9wZXJhdG9yLWthNGk5aDFAc2Euc3RhY2tpdC5jbG91ZCIsCiJzdGFja2l0X2ttc19rZXlyaW5nX2lkIjogImI1ZWFiNDYwLTRhYTEtNGRlOC04MjFjLTA2YzdiYTEyZTRmYyIsCiJzdGFja2l0X2ttc19rZXlfaWQiOiAiNjljYjM1ZmYtZmY2NC00YjQ0LThhZjItMGZjZDEzM2FmNTgzIiwKInN0YWNraXRfa21zX2tleV92ZXJzaW9uIjogIjEiLAoiZGF0YSI6ImRHVnpkQzF6WldOeVpYUT0ifQ==
This can now be stored into Barbican with the following command:
openstack secret store \
--name "MySecret" \
--payload-content-type "application/octet-stream" \
--payload-content-encoding "base64" \
--payload "eyJzdGFja2l0X2ttc19wcm90amVjdF9pZCI6ICI0Njc4MDNhOC05OTI1LTRmY2ItOTIyNS03OGVmMDQ3NDcxMWYiLAoic3RhY2tpdF9rbXNfcmVnaW9uIjogImV1MDEiLAoic3RhY2tpdF9zZXJ2aWNlX2FjY291bnQiOiAia21zLW9wZXJhdG9yLWthNGk5aDFAc2Euc3RhY2tpdC5jbG91ZCIsCiJzdGFja2l0X2ttc19rZXlyaW5nX2lkIjogImI1ZWFiNDYwLTRhYTEtNGRlOC04MjFjLTA2YzdiYTEyZTRmYyIsCiJzdGFja2l0X2ttc19rZXlfaWQiOiAiNjljYjM1ZmYtZmY2NC00YjQ0LThhZjItMGZjZDEzM2FmNTgzIiwKInN0YWNraXRfa21zX2tleV92ZXJzaW9uIjogIjEiLAoiZGF0YSI6ImRHVnpkQzF6WldOeVpYUT0ifQ=="
The base64 converted json becomes the payload
of this command. The flags --payload-content-type "application/octet-stream" --payload-content-encoding "base64"
are mandatory like this, so the secret has the correct format. When the secret is not defined as application/octet-stream
, the secret will be rejected by Cinder. name
can be freely chosen.
create encrypted volume
Normally when creating an encrypted volume in Openstack, Cinder would generate a new secret in Barbican and store it there afterwards. Vanilla Openstack currently doesn’t provide a the feature to use an already existing key from Barbican. The Cinder in YAOOK was modified to provide bring-your-own-key. Here the API also shouldn’t be modified, in order to keep the code-changes as minimal as possible. So the secret-id from Barbican will be provided to the Cinder-API via the properties for the new created volume.
Beginning of the volume-create command to create an encrypted volume with pre-created key would look like this:
openstack volume create --type LUKS --property encryption_key_id=b60f3d9a-018a-4e51-bd90-f6265be993ef ...
For --type
the encrypted volume type has to be defined, in order to tell cinder that the volume should be an encrypted volume. As --property
a key-value-pair encryption_key_id=SECRET_ID
has to be added. This secret-id is the uuid of the secret in Barbican. This uuid will be filtered in the Cinder-API and stored in the private encryption_key_id-field of the volume and will not be in the public metadata of the created volume.
When creating a volume this way, barbican will register a secret-consumer for this secret. For each additional backup or from snapshot of this volume create volume, a new secret-consumer will be created. Only when the last volume/backup related to this secret is deleted, the secret itself will be deleted by Cinder from Barbican.
Note
The deletion of secret-consumer in the current implementation in cinder is not fully free from race-conditions. The chance is very small, but still exist. So volumes and backups, which belongs to the same secret should be deleted one after another to avoid the risk.