Migrating secrets between secret stores¶
Barbican can run with more than one secret store plugin (for example software crypto, PKCS#11, and KMIP). Changing the global default or a project preferred store only affects new secrets. Existing payloads stay on the store that created them until they are migrated.
Use the migrate API to move a payload onto another configured store without changing the secret UUID. Consuming services that already hold the UUID do not need to be updated.
This is not the same as PKCS#11 MKEK rotation. barbican-manage hsm rewrap_pkek rewraps project KEKs after you rotate keys on the same HSM. It does not move a secret to a different backend.
When to migrate¶
Migrate when you need the same secret UUID on a different backend, for example:
Moving project secrets from a central HSM onto a project-dedicated HSM or KMIP server.
Moving a subset of secrets from software crypto onto PKCS#11.
Draining one backend so it can be removed.
Recreate (store a new secret and retarget consumers) when you cannot enable multiple backends, or when the secret has no payload (metadata-only secrets cannot be migrated).
Prerequisites¶
enable_multiple_secret_stores = Truewith at least two stores. See Using Secret Store Plugins in Barbican.A token that passes policy
secret:migrate_secretstore(default: projectadminto any store, or projectmemberonly to the project preferred store).The destination store listed by
GET /v1/secret-stores. See Secret Stores API - Reference.
Which tool to use¶
OSC (one secret, tenant or operator)¶
Project members and project administrators can migrate a single secret:
$ openstack secret migrate \
<secret-uuid> \
--secret-store <store-uuid>
Members may migrate only onto the project preferred store (or the global default when the project has no preferred store). Migrating to any other backend returns HTTP 403. Project admins may target any configured store.
This requires python-barbicanclient with microversion 1.3 support.
Operator command (one project, or one backend)¶
barbican-manage secret migrate runs on the Barbican API node. It
reads barbican.conf, queries the database for the secrets to
move, then calls plugin.resources.rewrap_secret
in-process (the same code path as the HTTP migrate API, without
Keystone or HTTP).
Use it to:
migrate every payload secret in a specified Keystone project
migrate every payload secret currently on a specified backend (all projects)
It continues after per-secret failures and never prints payloads. CLI flags, examples, and exit codes are in barbican-manage secret migrate.
API¶
Microversion 1.3:
PUT /v1/secrets/{secret-id}/secret-store/{secret-store-id}
OpenStack-API-Version: key-manager 1.3
The request body is empty. Success is 204 No Content. Full API
reference (including status codes and the computed
secret_store_id / secret_store_ref fields on secret GET/list):
PUT /v1/secrets/{uuid}/secret-store/{secret_store_id} and
Response Attributes (microversion 1.3+). Microversion summary:
REST API Version History.
The migrate API does not persist a secret_store_id on the secret
row. After a successful migrate, the payload is stored only on the
destination plugin. Secret metadata GET (microversion 1.3) always
returns computed secret_store_id / secret_store_ref (null
when multiple backends are disabled or the secret has no payload).
A live payload that cannot be mapped to exactly one catalogue store
is an error (HTTP 500).
Failure handling¶
A secret that is already on the destination store succeeds as a no-op on the API. The operator command skips those secrets and does not rewrap them.
Concurrent migrates of the same secret UUID are serialized with a
SELECT ... FOR UPDATErow lock. The second request re-checks after the lock and no-ops when the first already moved the payload.Metadata-only secrets return
400. The operator command records them as failures and continues.Decrypt or destination
store_secretfailures leave the secret on the source store (no database rewrite has started yet).If a migrate fails after the destination plugin accepted the payload (plugin or database error), the API rolls back the Barbican database change. The leftover destination plugin object is recorded as a
secret_store_cleanup_tasksrow. The retry scheduler enqueuesbarbican-workerto delete it (with further retries on failure). The secret remains on the source store.After a successful migrate, a cleanup task is always scheduled for the source plugin object (including
store_crypto, wheredelete_secretis a no-op). The HTTP response does not wait for that delete; the retry scheduler drives worker cleanup, and a plugin delete failure is retried rather than failing the migrate.Unexpected errors outside the plugin/database path do not schedule destination cleanup. The secret remains on the source store if the request transaction rolls back.
barbican-manage secret migratecontinues with remaining secrets. It writes one JSON object per failure tobarbican-manage-secret-migrate-errors.jsonl(or--error-file) and exits1if any secret failed on a live run.--dry-runstill records discover failures in that file but exits0.