OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Services

barbican — Summary

Reverse-specification of barbican — branch stable/2026.2

Branch stable/2026.2 @ de32745a7a4f — repository https://opendev.org/openstack/barbican — analysed on 2026-10-29

  • Repository: https://opendev.org/openstack/barbican (local clone) — specified branch: stable/2026.2 @ de32745a7a4f77c98dac5906af128dac577750bf (tag 23.0.0 + backports) — analysed on: 2026-10-29.
  • Scope: the whole service: barbican-api, barbican-worker, barbican-retry, barbican-keystone-listener, barbican-manage (and the legacy barbican-db-manage, pkcs11-*, barbican-status scripts); the secret-store plugin contract (store_crypto, KMIP, Vault, Dogtag) and the crypto plugin contract (simple_crypto, PKCS#11); the rewrite pack (step 8). The change history (step 7) covers the 2026.2 cycle since 22.0.0.
  • Clients whose expectations are in the pack: castellan stable/2026.2 @ bd6bd342982d (used by Nova, Cinder, Glance), python-barbicanclient stable/2026.2 @ 8162006 (and the openstack secret … OSC commands), Skyline (skyline-apiserver @ 4af72bfd4577, skyline-console @ 806d2003), barbican-tempest-plugin master @ f370cf0.
FileContent
01-rest-api_stable-2026.2.mdthe 57 operations of /v1, technical sheets and functional descriptions, lifecycles
02-errors_stable-2026.2.mderror body, exception catalogue, pecan.abort sites, errors outside the API
03-interactions_stable-2026.2.mddatabase, AMQP (worker RPC, Keystone notifications), secret-store back ends (HSM, KMIP, Vault, Dogtag), Keystone
04-scenarios_stable-2026.2.mdsequence diagrams: store and read a secret, key order, delete with consumers, project deletion, KEK rotation
05-async-processing_stable-2026.2.mdworker, retry scheduler, Keystone listener, maintenance commands
06-change-history_stable-2026.2.md2026.2 cycle (22.0.0..stable/2026.2, 47 commits) as change specifications and a carry-over plan
07-rewrite-pack_stable-2026.2.mdrewrite pack: encryption at rest, contracts, conformance data, client expectations
_rewrite/machine-readable contracts, captures, test vectors and tools
_verification_stable-2026.2.mdevery file:line reference mapped to the function it points into

Overview

Barbican is OpenStack's key manager. Projects store secrets (opaque data, passphrases, symmetric keys, public/private keys, certificates), group them in containers (generic, RSA, certificate), and ask Barbican to generate keys through asynchronous orders. Other services register as consumers of a secret or container, so that the owner sees who uses it and, from microversion 1.2, cannot delete a secret still in use without force. Access is project-based through oslo.policy, refined per secret/container by ACLs (per-operation user lists and a project-access flag). Per-project quotas cap the number of secrets, orders, containers and consumers.

The central property is encryption at rest: Barbican never stores a payload in clear. A secret-store plugin either keeps the secret in an external system (KMIP server, HashiCorp Vault through castellan, Dogtag KRA) and only records a reference, or — the default, store_crypto — encrypts it with a crypto plugin and stores the ciphertext in its own database. The crypto plugin uses a per-project KEK (key-encryption key), itself protected by a master key: a Fernet key in the configuration for simple_crypto, an AES key inside an HSM for p11_crypto. File 07 § 3 gives the exact byte layouts and an executable reference that decrypts every secret written by Python Barbican.

Architecture

flowchart LR
  cli[Clients: castellan in Nova/Cinder/Glance,<br/>barbicanclient/OSC, Skyline] -->|HTTPS /v1, X-Auth-Token,<br/>OpenStack-API-Version: key-manager 1.x| api[barbican-api<br/>Pecan behind paste pipeline]
  api -->|validate token| ks[Keystone]
  api -->|SQL| db[(MariaDB barbican)]
  api -->|AMQP cast process_type_order<br/>topic barbican.workers| mq{{RabbitMQ}}
  mq --> wk[barbican-worker]
  wk -->|SQL| db
  rt[barbican-retry] -->|SQL order_retry_tasks| db
  rt -->|AMQP cast| mq
  ksn[Keystone notifications<br/>identity.project.deleted] -->|AMQP exchange keystone<br/>topic notifications| kl[barbican-keystone-listener]
  kl -->|SQL soft delete| db
  api -->|crypto plugin| hsm[(HSM PKCS#11)]
  api -->|secret store| ext[(KMIP / Vault / Dogtag)]
  wk --> hsm
  wk --> ext
  mg[barbican-manage] -->|SQL, HSM| db

Processes

ProcessEntry pointRoleListens to / consumes
barbican-apibarbican/api/app.py:105 create_main_app (paste etc/barbican/barbican-api-paste.ini:5, served by uWSGI/mod_wsgi through barbican/api/app.py:117)REST API /v1, version discovery /, /healthcheckHTTP (default port 9311 [inferred from barbican/common/config.py:74 sample])
barbican-workerbarbican/cmd/worker.py:34 → barbican/queue/server.py:214 TaskServerprocesses orders (key and asymmetric generation)AMQP RPC topic barbican.workers, server barbican.queue (barbican/common/config.py:125)
barbican-retrybarbican/cmd/retry_scheduler.py:34 → barbican/queue/retry_scheduler.py:45 PeriodicServerre-enqueues order tasks scheduled for retrytimer ([retry_scheduler], 10 s)
barbican-keystone-listenerbarbican/cmd/keystone_listener.py:33 → barbican/queue/keystone_listener.py:139 MessageServerdeletes a project's Barbican data when Keystone deletes the projectAMQP notifications, exchange keystone, topic notifications (barbican/common/config.py:142)
barbican-managebarbican/cmd/barbican_manage.py:452db (upgrade, clean, sync_secret_stores…), hsm (MKEK/HMAC keys, rewrap), simple_crypto (new/rewrap project KEKs, rewrap secrets)CLI

Without [queue] enable = True (default False, barbican/common/config.py:120) there is no worker: the API runs the order task in-process right after committing the order (barbican/queue/client.py:39, barbican/queue/client.py:76).

External dependencies

SystemProtocolLibraryConfigurationUsage
MariaDB/MySQL (PostgreSQL, SQLite in tests)SQLSQLAlchemy 2 / oslo.db, Alembic[database] connectionall state; 26 tables
KeystoneHTTPSkeystonemiddleware auth_token[keystone_authtoken]token validation in the paste pipeline; source of identity.project.deleted notifications
RabbitMQAMQP 0-9-1oslo.messaging[DEFAULT] transport_url, [queue], [keystone_notifications]worker RPC casts; Keystone notifications
HSMPKCS#11 (C API through cffi)barbican/plugin/crypto/pkcs11.py[p11_crypto_plugin]master KEK and HMAC key, project-KEK wrapping, secret encryption
KMIP serverKMIP over TLSPyKMIP ProxyKmipClient[kmip_plugin]secret storage/generation (optional)
HashiCorp VaultHTTPS (KV v1/v2)castellan VaultKeyManager[vault_plugin]secret storage/generation (optional)
Dogtag KRAHTTPSdogtag-pki[dogtag_plugin]secret storage/generation (optional, legacy)

Key figures

  • 57 operations on 29 paths (_rewrite/routes_stable-2026.2.json), microversions 1.0–1.2, 82 default policy rules, 11 request JSON schemas, 26 tables (migrations and models identical, _rewrite/schema-diff_stable-2026.2.md).
  • Conformance data: Barbican's own black-box suite (functionaltests/) run against the real paste pipeline and a real Keystone — 563 tests pass, 69 skipped, 0 failures, 2,792 exchanges; in-process API unit tests — 435 tests, 659 exchanges.
  • Encryption at rest: the reference decryptor (_rewrite/tools/at_rest_reference.py) decrypted 665/665 secrets written by Python Barbican during that run byte-for-byte as Barbican does; PKCS#11 layouts checked over a software HSM emulation (8/8).

Points of attention

  • Encryption at rest is bound to Python class names. kek_data.plugin_name and the plugin_name row of secret_store_metadata hold fully qualified Python class names (barbican.plugin.crypto.simple_crypto.SimpleCryptoPlugin, barbican.plugin.store_crypto.StoreCryptoAdapterPlugin), computed by barbican/common/utils.py:170 and used to find the plugin at read time (barbican/plugin/crypto/manager.py:124, barbican/plugin/interface/secret_store.py:622). A rewrite must keep these strings as identifiers. See 07 § 3.
  • Two base64 layers. The crypto plugin receives raw bytes, but the database column holds base64(plugin ciphertext); for simple_crypto the plugin ciphertext is itself a url-safe-base64 Fernet token (barbican/plugin/store_crypto.py:86, barbican/plugin/store_crypto.py:314).
  • Soft delete keeps ciphertexts. Deleting a secret only sets deleted on the secret and its encrypted_data rows (barbican/model/models.py:212); ciphertexts stay until barbican-manage db clean (barbican/model/clean.py:458). External stores are deleted immediately (barbican/plugin/resources.py:265).
  • Each plugin parses its own configuration at import time (barbican/plugin/crypto/simple_crypto.py:29, barbican/common/config.py:221), from the default oslo.config locations, not from the --config-file of the process.
  • **Wrong Location on consumer registration**: POST /v1/containers/{id}/consumers answers Location: …/v1/consumers/{container_id}/consumers (barbican/common/hrefs.py:46), seen in the functional capture.
  • Non-GET methods accepted where no method dispatch exists: /v1/secrets/{id}/payload rejects non-GET itself (barbican/api/controllers/secrets.py:204), but /v1/secret-stores/global-default and /preferred answer any method with the GET behaviour (barbican/api/controllers/secretstores.py:187, barbican/api/controllers/secretstores.py:202).
  • **GET /v1/secrets/{id} with a non-JSON Accept** returns the decrypted payload under the stricter secret:decrypt policy (deprecated path, barbican/api/controllers/__init__.py:57, barbican/api/controllers/secrets.py:128).
  • Orders without a worker run synchronously inside the API request and any failure is swallowed (barbican/queue/client.py:76): the client sees 202 and must poll the order to find ERROR.
  • Keystone listener error path is broken: handle_error reads project.project_id, which the Project model does not have (barbican/tasks/keystone_consumer.py:79, barbican/model/models.py:272); the exception is caught and logged (barbican/tasks/resources.py:106).
  • Quota of 0 disables the resource; negative values mean unlimited (barbican/common/quota.py:190).

Blind spots / [inferred]

  • HSMs were not available: the PKCS#11 byte layouts come from the code and were exercised through a software emulation of the mechanisms (_rewrite/tools/at_rest_reference.py), not against a real token. Vendor-specific behaviour (e.g. HSM-generated GCM IVs, encryption_gen_iv=False) is [inferred].
  • KMIP, Vault and Dogtag back ends were read, not run. Their wire formats belong to PyKMIP, castellan and dogtag-pki.
  • Multiple secret stores ([secretstore] enable_multiple_secret_stores): 30 functional tests that need it were skipped; the behaviour is described from the code.
  • Certificate orders and CAs: the code paths were removed, but the tables (certificate_authorities, order_plugin_metadata…) and some exceptions remain (dead data model).
  • barbican-tempest-plugin was not run (needs a full cloud); its expectations were read.

Source: reverse-spec-barbican/00-summary_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.