Trilio Share Protection Backup and Recovery as a Service for OpenStack Manila shares
Guide

Prerequisites

Required tools, accounts, and access credentials


Overview

This page lists every tool, account, and credential you need before you can call the Trilio Share Protection (Abacá) API or deploy any part of the control plane. Satisfying these prerequisites up front prevents the most common setup failures — missing services, wrong versions, or credentials that lack the exact permissions Abacá expects. The requirements are split into three groups: the OpenStack services that must be running in your environment, the S3 bucket you will register as a backup target, and the client-side tooling you need on your workstation.


Prerequisites

OpenStack environment

Your target OpenStack cluster must be running the following services at the versions shown:

ServiceMinimum versionWhy Abacá needs it
KeystoneOpenStack ≥ 2023.1 (Antelope) / RHOSO 18Authenticates every API call; issues the Keystone trust that lets the conductor act on your behalf
BarbicanSame release as KeystoneStores your S3 credentials and Kopia repository password as tenant-owned secrets; Abacá never holds these values itself
ManilaSame releaseThe source of the file shares you want to protect
MariaDB≥ 10.x (Kolla 2024.2+)Abacá's bookkeeping database (schema: abaca)
RabbitMQSame release as OpenStackConductor RPC transport (vhost: /abaca, topic: abaca-conductor)
Nova + NeutronSame releaseRequired only when using the AttachToShareNetwork network attachment strategy (DHSS=true shares); not required for StaticReachability

The Kolla-Ansible globals must include enable_barbican: "yes". For DHSS=true share protection, you also need enable_manila: "yes" with an appropriately configured backend.

API access

Before calling the Abacá API you will need:

  • Network access to the Abacá service endpoint — the abaca-api container listens on port 9797 on the controller host (e.g. http://<controller>:9797). The endpoint is also registered in the Keystone service catalog under the service name share-protection. Confirm reachability with a quick health check:
    curl -sf http://<controller>:9797/healthcheck
    
  • An authentication token — see Authentication. All API calls require a valid Keystone token in the X-Auth-Token header.
  • An HTTP clientcurl, Postman, or a language-specific SDK such as the python-abacaclient library.

S3 backup target bucket

Every backup target requires a dedicated S3-compatible bucket that satisfies all of the following before enrollment:

  • Dedicated bucket — the bucket must not be shared with other application data.
  • Versioning enabled — required for object lock.
  • Object lock enabled at creation time — object lock cannot be added to an existing bucket; the bucket must be recreated if it was created without it.
  • No lifecycle expiration rules — expiration rules will silently delete backup objects and corrupt the Kopia repository.

Abacá runs automated preflight conformance checks (reachability, read/write round-trip, addressing mode, object-lock detection, lifecycle-rule scan, and bucket-contents inspection) during the enrollment process and refuses to accept a bucket that fails any check.

Supported S3-compatible providers: AWS S3, MinIO, Ceph RGW, ODF, Wasabi.

S3 credentials in Barbican

You must store your S3 access key and secret key as separate secrets in Barbican before calling the target registration endpoint. Abacá accepts only Barbican secret href references — it will never accept raw credential values in an API request body. Your Barbican tenant user needs the creator role (the default Kolla policy requires it for POST /v1/secrets).

Workstation tooling

ToolVersionRequired for
Python≥ 3.11 (3.12 recommended)python-abacaclient SDK and openstack CLI
python-abacaclientGA releaseopenstack share protection … CLI commands and Python SDK calls
openstackclientAny version compatible with your OpenStack releaseCLI access to Keystone, Manila, Barbican
Docker or PodmanAny recent stableLocal dev stack via Docker Compose only; not required for production
libguestfs-tools + qemu-utilsAny recent stableBuilding the worker VM Glance image; build-worker-image.sh installs these on first run

Control plane (operators only)

If you are deploying the Abacá control plane rather than just consuming the API, you additionally need:

  • OpenShift ≥ 4.12 for the production control-plane deployment.
  • A RHEL worker VM image with Kopia ≥ 0.23.1 baked in, uploaded to Glance as abaca-worker-<KOPIA_VERSION>. This image must have the abaca-worker-agent baked in (abaca_worker_agent_baked=1). See deploy/kolla/build-worker-image.sh.
  • An admin openrc for the target cluster, sourced in your shell. All deploy scripts require it.
  • ~5 GB free disk space on the controller (Docker images + Kolla logs).
  • SSH access from your workstation to the controller as a user with sudo. The scripts default to user pureuser; override with ABACA_SSH_USER / ABACA_SUDO_PASSWORD.

Installation

These steps bring up the Abacá control plane on a Kolla-Ansible OpenStack cluster and verify that it is ready to accept API calls. If you are only consuming an already-running API, skip to step 6 (verify the endpoint) and step 7 (install the client).

Step 1 — Source your admin openrc

All bootstrap scripts require an OpenStack admin credential. Source it once in the shell you will use for the remaining steps:

source cluster1-openrc.sh

Step 2 — Run the one-shot installer

From the repository root, run the installer. It executes steps 01–08 in order and is fully idempotent — safe to re-run:

bash deploy/kolla/install.sh

The installer performs these steps internally:

StepWhat it does
01-identityCreates the abaca Keystone service user (roles: service, admin) and a dedicated abaca worker project
02-catalogRegisters the share-protection service in the Keystone catalog with public, internal, and admin endpoints on port 9797
03-databaseCreates the abaca MariaDB database and user with a fresh random password; writes credentials to cluster1-abaca-secrets.env
04-messagingCreates the /abaca RabbitMQ vhost and user with a fresh random password; writes credentials to cluster1-abaca-secrets.env
05-buildSyncs source to the controller and builds the abaca_api and abaca_conductor container images locally
06-configRenders /etc/kolla/abaca-api/abaca.conf and /etc/kolla/abaca-conductor/abaca.conf
07-deployRuns database migrations (db_sync), starts or restarts the containers, and waits for /healthcheck to return 200
08-dashboardInstalls the abaca-dashboard Horizon plugin into the running horizon container

To re-run only specific steps (for example, a fast dev loop that just rebuilds and restarts containers):

bash deploy/kolla/install.sh \
    --skip-identity --skip-catalog --skip-db --skip-mq \
    --skip-config --skip-dashboard

Step 3 — Bootstrap the workstation-side Python venv (if needed)

If you do not already have openstackclient on your PATH, bootstrap the local venv:

bash deploy/cluster1/bootstrap.sh

This installs openstackclient, python-manilaclient, and python-abacaclient into deploy/cluster1/.venv.

Step 4 — Build the worker VM Glance image

Abacá dispatches backup and restore work to short-lived worker VMs. Build the worker image (first run downloads ~700 MB):

bash deploy/kolla/build-worker-image.sh

The image is uploaded to Glance as abaca-worker-<KOPIA_VERSION> (currently Kopia 0.23.1). To force a rebuild:

bash deploy/kolla/build-worker-image.sh --force

Step 5 — Boot a worker VM

You need at least one active worker VM before tenants can enroll a backup target. Read the transport URL from the controller and boot a worker:

export ABACA_TRANSPORT_URL=$(ssh <controller> \
    "sudo grep '^transport_url' /etc/kolla/abaca-conductor/abaca.conf" \
    | cut -d' ' -f3)

OS_CLOUD=abaca-service abaca-dev worker-boot \
    --network dr-net --key-name abaca-worker

Confirm the worker registered and is active:

openstack --os-cloud abaca-service share protection worker list

You should see one row with state=active and a last_heartbeat_at timestamp less than one minute old.

Step 6 — Verify the API endpoint

Confirm the abaca-api container is healthy:

curl -sf http://<controller>:9797/healthcheck

A 200 OK response confirms the control plane is up and accepting requests.

Step 7 — Install the Python client

Install python-abacaclient into your working Python environment (≥ 3.11):

pip install python-abacaclient

Verify the OpenStack CLI plugin loaded correctly:

openstack share protection --help

Step 8 — (Optional) Set up a local dev S3 backend with MinIO

For local end-to-end testing without an external S3 provider, deploy MinIO on the controller:

bash deploy/kolla/minio.sh

This creates two buckets on the controller at port 9000: abaca-demo-plain (no versioning) and abaca-demo-locked (versioning + COMPLIANCE object lock with a 1-day default retention).

Step 9 — (Optional) Generate clouds.yaml

Generate a workstation-side ~/.config/openstack/clouds.yaml with three named clouds (abaca-admin, abaca-service, abaca-tenant) from the openrc files:

bash deploy/kolla/clouds-yaml.sh

Use OS_CLOUD=abaca-tenant when running tenant-side operations such as target enrollment.


Configuration

Abacá's runtime configuration lives in /etc/kolla/abaca-api/abaca.conf and /etc/kolla/abaca-conductor/abaca.conf on the controller. Both files are rendered by deploy/kolla/06-config.sh from your cluster1-abaca-secrets.env file. After editing a config file, restart the affected container for the change to take effect:

docker restart abaca_api abaca_conductor

Key configuration options

[kopia] executor

ValueDefaultWhen to use
worker_rpcYesProduction. Every Kopia command is dispatched to a fleet-picked worker VM over RabbitMQ. The conductor never holds tenant S3 credentials or the repository password.
ephemeral_containerNoDev/local stack only (deploy/docker-compose.dev.yml). Runs Kopia in a short-lived local container. Logs a startup WARNING. Not for production.

The production installer (deploy/kolla/06-config.sh) always sets executor = worker_rpc.

[kopia] subprocess_timeout

The maximum number of seconds the conductor waits for a Kopia subprocess (on the worker or in a container) to complete before killing it and raising KopiaTimeout. A timeout maps the job to operator_action_required in error_category. Tune this if your shares are very large or your S3 endpoint is slow.

[enrollment] dispatch

ValueDefaultEffect
rpcYesThe API casts the job to the conductor via oslo.messaging on the abaca-conductor topic. job.executed_by is stamped conductor:<hostname>.
inlineNoUnit-test harness only. The API drives the state machine on its own thread. Starting with dispatch=inline while api.noauth=False is refused at startup.

[api] noauth

When False (the default and only production-safe value), every inbound request must carry a valid Keystone token. Setting noauth=True disables authentication entirely; this is only used in isolated unit-test harnesses and is refused in combination with dispatch=inline as described above.

S3 bucket least-privilege policy

When you create your S3 bucket, attach a bucket policy that grants Abacá exactly the actions it needs and nothing more. Replace TENANT-ABACA-BUCKET with your actual bucket name:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AbacaObjectDataPlane",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion"
      ],
      "Resource": "arn:aws:s3:::TENANT-ABACA-BUCKET/*"
    },
    {
      "Sid": "AbacaBucketControlPlane",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:ListBucketVersions",
        "s3:GetBucketVersioning",
        "s3:GetBucketObjectLockConfiguration",
        "s3:GetObjectLegalHold",
        "s3:PutObjectLegalHold",
        "s3:GetObjectRetention",
        "s3:PutObjectRetention"
      ],
      "Resource": "arn:aws:s3:::TENANT-ABACA-BUCKET"
    }
  ]
}

Do not grant s3:CreateBucket, s3:PutLifecycleConfiguration, s3:PutBucketVersioning, or any wildcard s3:*. Bucket creation and object-lock enablement are one-time manual actions, not runtime Abacá privileges.

On non-AWS providers (MinIO, Ceph RGW, ODF, Wasabi), map the equivalent actions; the intent — object read/write/delete plus versioning and object-lock introspection, scoped to a single bucket — is what matters.

FIPS mode

FIPS mode is a supported, validated production configuration. When FIPS mode is enabled, Abacá configures Kopia to use only FIPS-approved algorithms (AES256-GCM-HMAC-SHA256 encryption, HMAC-SHA256-128 block hash, pbkdf2 key derivation) and runs worker VMs in RHEL FIPS mode. If the pinned Kopia binary does not support --key-derivation-algorithm (Kopia < 0.14 or a stripped build), the conductor records fips_kdf_gap: true on the target's capabilities and falls back to scrypt, which is not FIPS-140 compliant. The fix is to rebuild the worker image with Kopia ≥ 0.23.1 and re-enroll the target.

Environment variables for deploy scripts

The deploy scripts read the following environment variables (override as needed; do not hardcode passwords):

VariableDefaultPurpose
ABACA_SSH_USERpureuserSSH user on the controller
ABACA_SUDO_PASSWORDpureusersudo password for that user
ABACA_TARGET_HOSTcontroller hostnameHost where containers run
ABACA_PUBLIC_URLhttp://<VIP>:9797Public Keystone endpoint URL
ABACA_INTERNAL_URLhttp://<VIP>:9797Internal Keystone endpoint URL
ABACA_ADMIN_URLhttp://<VIP>:9797Admin Keystone endpoint URL

Generated passwords (for the abaca database user and the /abaca RabbitMQ user) are written to cluster1-abaca-secrets.env at the repository root (mode 0600, gitignored) by steps 03 and 04 of the installer.


Usage

Authenticating

All API calls require a Keystone token obtained from your OpenStack identity endpoint. Include it in every request as the X-Auth-Token header:

# Obtain a token
export OS_TOKEN=$(openstack --os-cloud abaca-tenant token issue -f value -c id)

# Use it in a request
curl -sS http://<controller>:9797/v1/targets \
  -H "X-Auth-Token: ${OS_TOKEN}"

Tokens are short-lived. If you receive a 401 Unauthorized, request a new token and retry.

Storing S3 credentials in Barbican

Before registering a backup target, store your S3 access key and secret key as separate Barbican secrets. Abacá only accepts Barbican href references — never raw values:

# Store the access key
openstack --os-cloud abaca-tenant secret store \
  --name abaca-s3-access-key \
  --secret-type opaque \
  --payload "<YOUR_ACCESS_KEY>"

# Store the secret key
openstack --os-cloud abaca-tenant secret store \
  --name abaca-s3-secret-key \
  --secret-type opaque \
  --payload "<YOUR_SECRET_KEY>"

Note the secret_href returned for each. You will pass these hrefs when creating a backup target.

Registering a backup target

Use the python-abacaclient CLI to register a backup target. This triggers enrollment — Abacá runs preflight conformance checks against your bucket and initializes the Kopia repository:

export OS_CLOUD=abaca-tenant

openstack share protection target create \
  --name my-backup-target \
  --endpoint https://s3.example.com \
  --bucket my-abaca-bucket \
  --region us-east-1 \
  --access-key-ref <access_key_secret_href> \
  --secret-key-ref <secret_key_secret_href>

The API responds with 202 Accepted and returns both the target record and a job ID. Enrollment is asynchronous; poll the job until it reaches a terminal state:

openstack share protection job show <job_id>

The job moves through these states: queued → provisioning_network → provisioning_source → connecting_repository → transferring → finalizing → releasing → available (or error). When the job reaches available, the target is ready.

Creating a protection policy

A protection policy binds a share to a target and defines the backup schedule and retention rules:

openstack share protection policy create \
  --name daily-policy \
  --share-id <manila_share_id> \
  --target-id <target_id> \
  --schedule "0 2 * * *" \
  --retention-daily 7 \
  --retention-weekly 4 \
  --retention-monthly 12

Once attached, backups run automatically according to the cron schedule.

Running an on-demand backup

To trigger a backup immediately, outside of the policy schedule:

openstack share protection backup create \
  --policy-id <policy_id>

This also returns 202 Accepted with a job ID. Poll the job to follow progress.

Restoring a share from backup

To restore a backup to a new share (the default):

openstack share protection restore create \
  --backup-id <backup_id>

To restore to a specific sub-path or single file, or back onto the original share, pass the appropriate options. Refer to openstack share protection restore create --help for the full option list.

Using the abaca-dev CLI for enrollment (developer workflow)

For the full enrollment flow in a development environment, use abaca-dev enroll. This handles storing credentials in Barbican, creating the Keystone trust, and calling the API in one step:

export OS_CLOUD=abaca-tenant

abaca-dev enroll \
  --target-name demo \
  --bucket abaca-demo-locked \
  --access-key <mc-user> \
  --secret-key <mc-pass>

The command prints the target and job IDs. Use openstack share protection job show <job_id> to poll until available.

Checking the API health endpoint

The /healthcheck endpoint requires no authentication and is useful for monitoring and readiness probes:

curl -sf http://<controller>:9797/healthcheck

Examples

Example 1 — Verify the control plane is reachable

Before any authenticated call, confirm the API is up:

curl -sf http://<controller>:9797/healthcheck

Expected output:

OK

A non-200 response means the abaca_api container is not running or the port is not reachable from your network. Check docker logs abaca_api on the controller.


Example 2 — Obtain a Keystone token and list backup targets

export OS_TOKEN=$(openstack --os-cloud abaca-tenant token issue -f value -c id)

curl -sS http://<controller>:9797/v1/targets \
  -H "X-Auth-Token: ${OS_TOKEN}" | python3 -m json.tool

Expected output (empty tenant):

{
  "targets": []
}

Example 3 — Store S3 credentials in Barbican and register a backup target

export OS_CLOUD=abaca-tenant

# 1. Store credentials
ACCESS_HREF=$(openstack secret store \
  --name abaca-s3-access-key \
  --secret-type opaque \
  --payload "AKIAIOSFODNN7EXAMPLE" \
  -f value -c "Secret href")

SECRET_HREF=$(openstack secret store \
  --name abaca-s3-secret-key \
  --secret-type opaque \
  --payload "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
  -f value -c "Secret href")

# 2. Register the target (triggers enrollment)
openstack share protection target create \
  --name my-backup-target \
  --endpoint https://s3.us-east-1.amazonaws.com \
  --bucket my-abaca-bucket \
  --region us-east-1 \
  --access-key-ref "${ACCESS_HREF}" \
  --secret-key-ref "${SECRET_HREF}"

Expected output (abbreviated):

{
  "target": {
    "id": "a1b2c3d4-...",
    "name": "my-backup-target",
    "status": "enrolling"
  },
  "job": {
    "id": "e5f6a7b8-...",
    "type": "target_enroll",
    "state": "queued"
  }
}

Example 4 — Poll a job until it reaches a terminal state

JOB_ID="e5f6a7b8-..."

while true; do
  STATE=$(openstack --os-cloud abaca-tenant share protection job show "${JOB_ID}" \
    -f value -c state)
  echo "$(date -u +%H:%M:%S) state=${STATE}"
  case "${STATE}" in
    available|error) break ;;
  esac
  sleep 5
done

Expected output (successful enrollment):

10:01:03 state=queued
10:01:08 state=provisioning_network
10:01:13 state=connecting_repository
10:01:23 state=transferring
10:01:35 state=finalizing
10:01:38 state=releasing
10:01:40 state=available

If the final state is error, retrieve the error_category and error_message fields from the job to determine who needs to act:

openstack --os-cloud abaca-tenant share protection job show "${JOB_ID}"

Example 5 — Create a protection policy and trigger an on-demand backup

export OS_CLOUD=abaca-tenant

# Create the policy (runs daily at 02:00 UTC)
POLICY_ID=$(openstack share protection policy create \
  --name daily-policy \
  --share-id "<manila_share_id>" \
  --target-id "a1b2c3d4-..." \
  --schedule "0 2 * * *" \
  --retention-daily 7 \
  --retention-weekly 4 \
  --retention-monthly 12 \
  -f value -c id)

echo "Policy created: ${POLICY_ID}"

# Trigger an immediate backup
openstack share protection backup create --policy-id "${POLICY_ID}"

Expected output:

{
  "backup": {
    "id": "c9d0e1f2-...",
    "status": "creating"
  },
  "job": {
    "id": "a3b4c5d6-...",
    "type": "backup",
    "state": "queued"
  }
}

Example 6 — List active workers (operators)

curl -sS http://<controller>:9797/v1/admin/workers \
  -H "X-Auth-Token: ${OS_TOKEN}" | python3 -m json.tool

Expected output:

{
  "workers": [
    {
      "id": "f7e8d9c0-...",
      "state": "active",
      "last_heartbeat_at": "2024-11-01T10:01:55Z",
      "capacity_slots": 4,
      "running_jobs": 0
    }
  ]
}

Troubleshooting

Use the following pattern to diagnose failures: check the job's state, error_category, and error_message first, then look at container logs on the controller.

# Check job details
openstack --os-cloud abaca-tenant share protection job show <job_id>

# Check container logs
docker logs abaca_api        # on the controller
docker logs abaca_conductor  # on the controller

Symptom: curl http://<controller>:9797/healthcheck returns 502 or 500.

Likely cause: The abaca_api or abaca_conductor container failed to start, usually because a database migration failed, the RabbitMQ vhost is missing, or there is a config file syntax error.

Fix:

  1. Check docker logs abaca_api and docker logs abaca_conductor for error messages.
  2. Re-run the database migration: bash deploy/kolla/install.sh --skip-identity --skip-catalog --skip-mq --skip-build --skip-dashboard (this re-runs step 07 which calls db_sync).
  3. Verify the RabbitMQ vhost exists: bash deploy/kolla/04-messaging.sh.
  4. Check for config syntax errors by reviewing /etc/kolla/abaca-api/abaca.conf and /etc/kolla/abaca-conductor/abaca.conf on the controller.

Symptom: Enrollment job reaches error with error_category: tenant_action_required.

Likely cause: Your S3 bucket fails a preflight conformance check. Common sub-causes:

  • Bucket has lifecycle expiration rules.
  • Bucket is not dedicated (contains foreign objects).
  • Barbican secret hrefs are invalid or the Keystone trust does not grant access to them.
  • S3 credentials cannot reach the bucket endpoint.

Fix:

  1. Read error_message on the job for the specific check that failed.
  2. For expiration rules: delete or disable all lifecycle expiration rules on the bucket, then re-enroll.
  3. For a mixed-use bucket: use a dedicated bucket; create a new one if necessary. Remember that object lock must be enabled at bucket creation time and cannot be added later.
  4. For credential issues: verify the Barbican secrets are accessible with your tenant token (openstack --os-cloud abaca-tenant secret get <href>) and that the Keystone trust was created with the correct role scoping.

Symptom: Enrollment job reaches error with error_category: operator_action_required.

Likely cause: Infrastructure problem — the conductor cannot reach the S3 endpoint from the worker's network, a Kopia subprocess timed out, or Barbican is unavailable.

Fix:

  1. Verify the worker VM can reach the S3 endpoint and the controller's RabbitMQ port (5672).
  2. If egress is blocked on your hypervisor, set up host-NAT egress: TENANT_NET_NAME=dr-net bash deploy/kolla/setup-host-nat-egress.sh.
  3. Check [kopia] subprocess_timeout — increase it for large shares or slow S3 endpoints.
  4. Check Barbican availability: openstack --os-cloud abaca-tenant secret list.

Symptom: Worker VM boots but never appears in openstack share protection worker list (or state stays provisioning).

Likely cause: The worker agent cannot connect to RabbitMQ, or the transport URL in the worker image does not match the conductor's.

Fix:

  1. SSH into the worker VM and check: journalctl -u abaca-worker-agent.
  2. Compare /etc/abaca/abaca.conf on the worker VM with /etc/kolla/abaca-conductor/abaca.conf on the controller — the transport_url values must match.
  3. Confirm the worker VM can reach the controller on port 5672: nc -zv <controller> 5672.
  4. If the Glance image predates the R2 worker-agent bake, rebuild it: bash deploy/kolla/build-worker-image.sh --force.

Symptom: 401 Unauthorized on every API call.

Likely cause: Your Keystone token has expired, or you are sending the token in the wrong header.

Fix:

  1. Request a new token: export OS_TOKEN=$(openstack --os-cloud abaca-tenant token issue -f value -c id).
  2. Confirm you are passing it as X-Auth-Token: ${OS_TOKEN}, not Authorization: Bearer.
  3. Verify the share-protection service endpoint is registered correctly: openstack endpoint list --service share-protection.

Symptom: A config change to /etc/kolla/abaca-api/abaca.conf or /etc/kolla/abaca-conductor/abaca.conf has no effect.

Likely cause: The config file is bind-mounted read-only into the container; the container must be restarted to pick up changes.

Fix:

docker restart abaca_api abaca_conductor

Symptom: Horizon Share Protection panels are missing after running 08-dashboard.sh.

Likely cause: The abaca-dashboard Horizon plugin installation is ephemeral. Running kolla-ansible reconfigure horizon or rebuilding the Horizon image reverts it.

Fix: Re-run the dashboard installer:

bash deploy/kolla/08-dashboard.sh

Note: a durable installation path (custom abaca-horizon image) is tracked in the project backlog. Treat this as a known limitation and re-run the script after any Horizon reconfigure.


Symptom: target.capabilities shows fips_kdf_gap: true after enrollment.

Likely cause: The Kopia binary baked into the worker image does not support --key-derivation-algorithm (Kopia < 0.14 or a stripped build). The repository was created with scrypt instead of pbkdf2, which is not FIPS-140 compliant.

Fix: Rebuild the worker image to include Kopia ≥ 0.23.1, then re-enroll the target so the new repository uses pbkdf2:

bash deploy/kolla/build-worker-image.sh --force

After the new image is in Glance, delete the affected target and re-enroll it.