---
title: Prerequisites
product: trilio-share-protection
doc_type: guide
version: master
source: git2docs (code-derived, validation-filtered)
canonical: https://git2docs.com/murali-balcha/docs/trilio-share-protection/trilio-share-protection-/prerequisites
---

# 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:

| Service | Minimum version | Why Abacá needs it |
|---|---|---|
| Keystone | OpenStack ≥ 2023.1 (Antelope) / RHOSO 18 | Authenticates every API call; issues the Keystone trust that lets the conductor act on your behalf |
| Barbican | Same release as Keystone | Stores your S3 credentials and Kopia repository password as tenant-owned secrets; Abacá never holds these values itself |
| Manila | Same release | The source of the file shares you want to protect |
| MariaDB | ≥ 10.x (Kolla 2024.2+) | Abacá's bookkeeping database (schema: `abaca`) |
| RabbitMQ | Same release as OpenStack | Conductor RPC transport (vhost: `/abaca`, topic: `abaca-conductor`) |
| Nova + Neutron | Same release | Required 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:
  ```bash
  curl -sf http://<controller>:9797/healthcheck
  ```
- **An authentication token** — see [Authentication](#authentication). All API calls require a valid Keystone token in the `X-Auth-Token` header.
- **An HTTP client** — `curl`, 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

| Tool | Version | Required for |
|---|---|---|
| Python | ≥ 3.11 (3.12 recommended) | `python-abacaclient` SDK and `openstack` CLI |
| `python-abacaclient` | GA release | `openstack share protection …` CLI commands and Python SDK calls |
| `openstackclient` | Any version compatible with your OpenStack release | CLI access to Keystone, Manila, Barbican |
| Docker or Podman | Any recent stable | Local dev stack via Docker Compose only; not required for production |
| `libguestfs-tools` + `qemu-utils` | Any recent stable | Building 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:

```bash
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
bash deploy/kolla/install.sh
```

The installer performs these steps internally:

| Step | What it does |
|---|---|
| 01-identity | Creates the `abaca` Keystone service user (roles: `service`, `admin`) and a dedicated `abaca` worker project |
| 02-catalog | Registers the `share-protection` service in the Keystone catalog with public, internal, and admin endpoints on port 9797 |
| 03-database | Creates the `abaca` MariaDB database and user with a fresh random password; writes credentials to `cluster1-abaca-secrets.env` |
| 04-messaging | Creates the `/abaca` RabbitMQ vhost and user with a fresh random password; writes credentials to `cluster1-abaca-secrets.env` |
| 05-build | Syncs source to the controller and builds the `abaca_api` and `abaca_conductor` container images locally |
| 06-config | Renders `/etc/kolla/abaca-api/abaca.conf` and `/etc/kolla/abaca-conductor/abaca.conf` |
| 07-deploy | Runs database migrations (`db_sync`), starts or restarts the containers, and waits for `/healthcheck` to return 200 |
| 08-dashboard | Installs 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
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
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
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
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:

```bash
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:

```bash
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:

```bash
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):

```bash
pip install python-abacaclient
```

Verify the OpenStack CLI plugin loaded correctly:

```bash
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
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
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:

```bash
docker restart abaca_api abaca_conductor
```

### Key configuration options

#### `[kopia] executor`

| Value | Default | When to use |
|---|---|---|
| `worker_rpc` | **Yes** | Production. 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_container` | No | Dev/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`

| Value | Default | Effect |
|---|---|---|
| `rpc` | **Yes** | The API casts the job to the conductor via oslo.messaging on the `abaca-conductor` topic. `job.executed_by` is stamped `conductor:<hostname>`. |
| `inline` | No | Unit-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:

```json
{
  "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):

| Variable | Default | Purpose |
|---|---|---|
| `ABACA_SSH_USER` | `pureuser` | SSH user on the controller |
| `ABACA_SUDO_PASSWORD` | `pureuser` | sudo password for that user |
| `ABACA_TARGET_HOST` | controller hostname | Host where containers run |
| `ABACA_PUBLIC_URL` | `http://<VIP>:9797` | Public Keystone endpoint URL |
| `ABACA_INTERNAL_URL` | `http://<VIP>:9797` | Internal Keystone endpoint URL |
| `ABACA_ADMIN_URL` | `http://<VIP>:9797` | Admin 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:

```bash
# 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:

```bash
# 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:

```bash
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:

```bash
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:

```bash
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:

```bash
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):

```bash
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:

```bash
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:

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

## Examples

### Example 1 — Verify the control plane is reachable

Before any authenticated call, confirm the API is up:

```bash
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

```bash
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):**
```json
{
  "targets": []
}
```

---

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

```bash
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):**
```json
{
  "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

```bash
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:

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

---

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

```bash
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:**
```json
{
  "backup": {
    "id": "c9d0e1f2-...",
    "status": "creating"
  },
  "job": {
    "id": "a3b4c5d6-...",
    "type": "backup",
    "state": "queued"
  }
}
```

---

### Example 6 — List active workers (operators)

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

**Expected output:**
```json
{
  "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.

```bash
# 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:*
```bash
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
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
bash deploy/kolla/build-worker-image.sh --force
```
After the new image is in Glance, delete the affected target and re-enroll it.
