#!/bin/sh # Idempotent MinIO bootstrap for the Familienarchiv stack. # # Runs on every `docker compose up` (the create-buckets service is one-shot, # no restart). Each step swallows the "already exists" error so the script # is safe to re-run. # # What it does: # 1. Register the MinIO alias using the root credentials # 2. Create the application bucket if missing # 3. Lock the bucket to private (defense in depth) # 4. Create/enable the `archiv-app` service account (least-privilege user) # 5. Install a bucket-scoped policy `archiv-app-policy`: # - GetObject/PutObject/DeleteObject on familienarchiv/* # - ListBucket + GetBucketLocation on familienarchiv # (Replaces MinIO's built-in `readwrite` which grants s3:* on *.) # 6. Attach the policy to `archiv-app` # 7. Fatal assertion: read back the user and confirm the policy is bound. # Uses `case` (POSIX) for substring match — the minio/mc image ships # coreutils + bash but NOT grep/awk/sed. # # Required env vars: MINIO_PASSWORD, MINIO_APP_PASSWORD set -e mc alias set myminio http://minio:9000 archiv "$MINIO_PASSWORD" mc mb myminio/familienarchiv --ignore-existing mc anonymous set private myminio/familienarchiv mc admin user add myminio archiv-app "$MINIO_APP_PASSWORD" \ || mc admin user enable myminio archiv-app cat > /tmp/archiv-app-policy.json <<'POLICY' { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Resource": ["arn:aws:s3:::familienarchiv/*"] }, { "Effect": "Allow", "Action": ["s3:ListBucket", "s3:GetBucketLocation"], "Resource": ["arn:aws:s3:::familienarchiv"] } ] } POLICY mc admin policy create myminio archiv-app-policy /tmp/archiv-app-policy.json 2>/dev/null \ || mc admin policy update myminio archiv-app-policy /tmp/archiv-app-policy.json mc admin policy attach myminio archiv-app-policy --user archiv-app 2>/dev/null || true INFO=$(mc admin user info myminio archiv-app) case "$INFO" in *archiv-app-policy*) echo "archiv-app bound to archiv-app-policy" ;; *) echo "FATAL: archiv-app is missing the bucket-scoped policy" echo "----- user info -----" echo "$INFO" exit 1 ;; esac