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

Reverse-spec · Services

barbican — Interactions

Interactions — barbican — branch stable/2026.2

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

1. Database

1.1 Access pattern

  • SQLAlchemy 2 ORM on an oslo.db engine (barbican/model/repositories.py:196) with a thread-local scoped_session (barbican/model/repositories.py:113). Every API request is one transaction: Pecan's TransactionHook calls repositories.start / commit / rollback / clear around the handler (barbican/api/hooks.py:41); a GET is "read only" for Pecan but uses the same session.
  • POST /v1/orders commits inside the handler before casting to the worker, so the worker can read the order (barbican/api/controllers/orders.py:197). Quotas are committed explicitly too (barbican/common/quota.py:112).
  • Workers wrap each task in its own transaction (barbican/queue/server.py:72); the Keystone listener and the retry scheduler do the same (barbican/tasks/keystone_consumer.py:47, barbican/queue/retry_scheduler.py:135).
  • No SELECT … FOR UPDATE anywhere. Concurrency is handled by unique constraints and "catch the duplicate, re-read": project creation (barbican/common/resources.py:52), consumer registration (barbican/model/repositories.py:1460). Concurrent KEK creation can leave several active KEKs; the oldest is kept, the others are deactivated on the next use (barbican/model/repositories.py:965).
  • Schema: [DEFAULT] db_auto_create = True creates the tables from the models and stamps Alembic head, or runs the upgrade when alembic_version exists (barbican/model/repositories.py:230); production runs barbican-manage db upgrade (barbican/cmd/barbican_manage.py:115). Alembic head on this branch: 8c74e2d7f1ff (3 revisions since the Ocata rebase).
  • Soft delete: most tables carry deleted/deleted_at (barbican/model/models.py:209); reads filter deleted = false; barbican-manage db clean purges (barbican/model/clean.py:458).

1.2 Tables

TableRoleWritten byRead by
projectsBarbican's view of a Keystone project (external_id unique)first request of a project (barbican/common/resources.py:34)every request
secretssecret metadata (no payload): name, type, algorithm, bit_length, mode, expiration, creator_idPOST /v1/secrets, orderssecrets API, containers
encrypted_dataciphertext of a secret for store_crypto: cypher_text, kek_meta_extended, content_type, kek_id → kek_databarbican/plugin/store_crypto.py:303barbican/plugin/store_crypto.py:119
kek_dataper-project, per-crypto-plugin KEK: plugin_name, kek_label, active, bind_completed, plugin_meta (wrapped KEK)barbican/model/repositories.py:970, barbican/plugin/store_crypto.py:297barbican/plugin/store_crypto.py:277
secret_store_metadatakey/value per secret: always plugin_name (store plugin class) and content_type; plus back-end references (key_uuid for KMIP, key_id/version for Vault…)barbican/plugin/resources.py:338barbican/plugin/resources.py:330
secret_user_metadatauser key/value metadatasecret metadata APIsecret metadata API
secret_acls, secret_acl_usersper-operation ACL with project_access, user listACL APIpolicy target (barbican/api/controllers/__init__.py:185), acl_only listing
containers, container_secretcontainers and their named secret linkscontainers API, asymmetric orders (barbican/plugin/resources.py:380)containers API
container_acls, container_acl_userscontainer ACLsACL APIpolicy target
container_consumer_metadata(container_id, name, URL) consumersconsumers APIcontainer representation
secret_consumer_metadata(secret_id, service, resource_type, resource_id) consumersconsumers APIsecret representation (1.1+), delete check (1.2+)
ordersasynchronous requests: type, meta (JSON), status, secret_id / container_id, error_status_code, error_reason, sub_status*orders API, workerorders API, worker
order_retry_tasksscheduled retries: retry_task, retry_at, retry_args, retry_kwargs (JSON)worker (barbican/queue/server.py:173)retry scheduler
order_plugin_metadata, order_barbican_metadatacertificate-order metadata— (feature removed)—
transport_keysplugin_name, transport_keytransport-key API, barbican/plugin/resources.py:41idem
project_quotasper-project quota overrides (NULL = default)project-quotas APIquota enforcement
secret_stores, project_secret_storeconfigured stores (multiple back ends) and a project's preferred storestart-up sync (barbican/plugin/util/multiple_backends.py:106), preferred APIstore selection
certificate_authorities, certificate_authority_metadata, project_certificate_authorities, preferred_certificate_authoritiescertificate authorities— (feature removed)delete_all_project_resources cascade only

The exact DDL is in _rewrite/schema-sqlite-migrated_stable-2026.2.sql (migrations, the reference) and _rewrite/schema-mysql_stable-2026.2.sql / schema-postgresql_stable-2026.2.sql (models); _rewrite/schema-diff_stable-2026.2.md shows no difference between models and migrations.

1.3 Representative queries

  • Secret list (barbican/model/repositories.py:590): sql SELECT secrets.* FROM secrets JOIN projects ON projects.id = secrets.project_id WHERE secrets.deleted = 0 AND (secrets.expiration IS NULL OR secrets.expiration > :now) AND secrets.name LIKE :name ... -- optional filters AND projects.external_id = :project ORDER BY ... LIMIT :limit OFFSET :offset; -- plus a COUNT(*) of the same query for "total" With acl_only=true the project join is replaced by JOIN secret_acls JOIN secret_acl_users WHERE secret_acl_users.user_id = :user (barbican/model/repositories.py:640).
  • Secret by id (barbican/model/repositories.py:759): by id, not deleted, not expired — without project filter (the project check is the policy's job, barbican/api/controllers/secrets.py:366).
  • Active KEK (barbican/model/repositories.py:1000): SELECT * FROM kek_data WHERE project_id = :p AND plugin_name = :fqcn AND active AND NOT deleted ORDER BY created_at.
  • Quota count: SELECT COUNT(*) of the project's non-deleted entities of the type (barbican/model/repositories.py:495).

2. AMQP / RabbitMQ (oslo.messaging)

2.1 Topology

ExchangeTypeRouting key / queueProducerConsumer
[DEFAULT] control_exchange (oslo default openstack)topicbarbican.workers ([queue] topic) and barbican.workers.barbican.queue (server)barbican-api, barbican-retrybarbican-worker (barbican/queue/__init__.py:62)
keystone ([keystone_notifications] control_exchange)topicnotifications.info ([keystone_notifications] topic + priority), optional pool pool_nameKeystonebarbican-keystone-listener (barbican/queue/__init__.py:88)

The RPC target is Target(topic='barbican.workers', namespace='barbican', version='1.1', server='barbican.queue') (barbican/common/config.py:116); the server uses DefaultRPCAccessPolicy (barbican/queue/__init__.py:80). Exceptions allowed to travel back: barbican.common.exception (barbican/queue/__init__.py:36).

2.2 RPC methods

MethodDirectionModeTopic / serverVersionArgumentsResponseDefined at
process_type_orderapi / retry → workercastbarbican.workers1.1order_id (Barbican UUID), project_id (Keystone project id), request_idnoneclient barbican/queue/client.py:41, server barbican/queue/server.py:205

There are no calls (barbican/queue/client.py:58 exists but is unused). When [queue] enable = False the same method is invoked in-process (barbican/queue/client.py:63).

2.3 Sample frame (cast process_type_order)

{"oslo.version": "2.0",
 "oslo.message": "{\"method\": \"process_type_order\", \"namespace\": \"barbican\", \"version\": \"1.1\",
   \"args\": {\"order_id\": \"5d1f6a8e-...\", \"project_id\": \"8c13fb9eea6a46388a3bd8316f26ce5c\",
              \"request_id\": \"req-a0e40e18-...\"}, \"_unique_id\": \"...\"}"}

The context is empty ({}, barbican/queue/client.py:56): no user token or roles travel with the task.

2.4 Consumed notification (Keystone)

event_typePriorityPayload usedEffectHandled at
identity.project.deletedinforesource_info (Keystone project id)delete all Barbican data of the projectbarbican/queue/keystone_listener.py:52
any otherany—ignored (returns None = acknowledged)barbican/queue/keystone_listener.py:104

Sample frame (Keystone basic notification format):

{"event_type": "identity.project.deleted", "publisher_id": "identity.keystone-host",
 "priority": "INFO", "payload": {"resource_info": "2b99a94ad02741978e613fb52dd1f4cd"},
 "message_id": "...", "timestamp": "2026-10-29 07:00:00.000000"}

Barbican emits no notifications of its own (the oslo.messaging notification options are only listed for the audit middleware, etc/oslo-config-generator/barbican.conf:15).

3. Keystone

  • Token validation by keystonemiddleware auth_token in the /v1 pipeline (etc/barbican/barbican-api-paste.ini:16, [keystone_authtoken]); it calls GET /v3/auth/tokens with its service token, caches results, and sets X-Identity-Status, X-Project-Id, X-User-Id, X-Roles, X-Service-*… that ContextMiddleware turns into a RequestContext (barbican/api/middleware/context.py:80). is_admin = the role [DEFAULT] admin_role (default admin) is in the token (barbican/api/middleware/context.py:87).
  • Optional keystonemiddleware.audit filter (pipeline barbican-api-keystone-audit, etc/barbican/barbican-api-paste.ini:20) emits CADF audit events with etc/barbican/api_audit_map.conf.
  • Barbican makes no other outbound Keystone call.

4. Secret-store and crypto back ends

4.1 Plugin contracts

Secret-store plugin (barbican/plugin/interface/secret_store.py:391, namespace barbican.secretstore.plugin, [secretstore] enabled_secretstore_plugins, default store_crypto):

MethodCalled whenReturns
get_plugin_name()listing storesdisplay name
store_secret_supports(key_spec) / generate_supports(key_spec)choosing a plugin, in configuration order (barbican/plugin/interface/secret_store.py:573, barbican/plugin/interface/secret_store.py:627)bool
store_secret(secret_dto)POST/PUT payload; secret_dto.secret is base64 text of the raw bytesdict saved to secret_store_metadata
get_secret(secret_type, metadata)GET payload; metadata = the secret's secret_store_metadataSecretDTO with base64 secret
generate_symmetric_key(key_spec) / generate_asymmetric_key(key_spec)ordersmetadata dict / AsymmetricKeyMetadataDTO
delete_secret(metadata)DELETE secret—
get_transport_key() / is_transport_key_current(key)transport-key flows (Dogtag only)key or None

The plugin that stored a secret is found again by its fully qualified class name saved in secret_store_metadata.plugin_name (barbican/plugin/resources.py:345, barbican/plugin/interface/secret_store.py:606).

Crypto plugin (barbican/plugin/crypto/base.py:241, namespace barbican.crypto.plugin, [crypto] enabled_crypto_plugins, default simple_crypto), used only through StoreCryptoAdapterPlugin (barbican/plugin/store_crypto.py:50):

MethodInputOutput
bind_kek_metadata(kek_meta_dto)an unbound kek_data rowthe DTO with algorithm, bit_length, mode, plugin_meta (wrapped project KEK)
encrypt(EncryptDTO(raw bytes), kek_meta_dto, external_project_id)raw secret bytesResponseDTO(cypher_text bytes, kek_meta_extended str|None)
decrypt(DecryptDTO(cypher bytes), kek_meta_dto, kek_meta_extended, external_project_id)raw bytes
generate_symmetric(GenerateDTO) / generate_asymmetric(GenerateDTO)algorithm, bit length, mode, passphraseResponseDTO / three ResponseDTO (private, public, passphrase)
supports(type, algorithm, bit_length, mode)ENCRYPT_DECRYPT, SYMMETRIC_KEY_GENERATION, ASYMMETRIC_KEY_GENERATIONbool

The exact at-rest formats of simple_crypto and p11_crypto are specified in 07 § 3.

4.2 Back ends

Back endProtocol / libraryStored in Barbican's DBConfigurationDefined at
simple_crypto (default)none (Fernet in-process)ciphertext + wrapped project KEK[simple_crypto_plugin] kek (MultiStr, Fernet keys)barbican/plugin/crypto/simple_crypto.py:55
p11_cryptoPKCS#11 C API via cffi: C_Initialize, C_OpenSession, C_Login, C_FindObjects (MKEK/HMAC by label), C_GenerateKey, C_WrapKey/C_UnwrapKey, C_SignInit/C_Sign/C_Verify (HMAC), C_Encrypt/C_Decrypt, C_GenerateRandom, C_SeedRandomciphertext + wrapped project KEK (JSON)[p11_crypto_plugin] (library_path, login, slot_id / token_serial_number / token_labels, mkek_label, hmac_label, mechanisms…)barbican/plugin/crypto/p11_crypto.py:134, barbican/plugin/crypto/pkcs11.py:451
KMIPKMIP 1.x over TLS, PyKMIP ProxyKmipClient (create, create_key_pair, register, get, destroy)secret_store_metadata.key_uuid[kmip_plugin] (host, port, username/password or client certificate, pkcs1_only)barbican/plugin/kmip_secret_store.py:151
VaultHTTPS KV v1/v2 through castellan VaultKeyManager: POST {vault_url}/v1/{kv_mountpoint}/[data/]{kv_path/}{uuid4hex} with {"type", "value": hex(bytes), "algorithm", "bit_length", "name", "created"} (wrapped in {"data": …} for v2), GET, DELETE (castellan:castellan/key_manager/vault_key_manager.py:386)secret_store_metadata.key_id (+ version: 1, alg, bit_length for generated pairs)[vault_plugin] (root_token_id or AppRole, vault_url, kv_mountpoint, kv_path, kv_version, namespace, ssl_ca_crt_file)barbican/plugin/vault_secret_store.py:77, barbican/plugin/castellan_secret_store.py:30
Dogtag KRAHTTPS, dogtag-pki clientKRA key ids[dogtag_plugin]barbican/plugin/dogtag.py:148

Vault secrets written before castellan metadata version 1 are read as legacy (base64 re-encoding heuristics, barbican/plugin/castellan_secret_store.py:57).

4.3 Multiple back ends

With [secretstore] enable_multiple_secret_stores = True, the stores are read from [secretstore:<suffix>] sections listed in stores_lookup_suffix (barbican/plugin/util/multiple_backends.py:32), exactly one of them global_default. New secrets go to the project's preferred store, else to the global default; reads always use the plugin recorded with the secret (barbican/plugin/util/multiple_backends.py:262).

5. Other

  • HSM key management CLI (barbican-manage hsm gen_mkek|check_mkek|gen_hmac|check_hmac|rewrap_pkek, barbican/cmd/barbican_manage.py:171): creates the token objects labelled mkek_label (AES, CKA_TOKEN, wrap/unwrap) and hmac_label (sign/verify).
  • No subprocess, no files: Barbican writes nothing to disk at run time (except logs); PKCS#11 may read [p11_crypto_plugin] seed_file at start (barbican/plugin/crypto/p11_crypto.py:347).
  • Healthcheck: GET /healthcheck, disabled by the file /etc/barbican/healthcheck_disable (etc/barbican/barbican-api-paste.ini:59).

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