All checks were successful
Deploy storage stack / deploy (push) Successful in 19s
asxpio's admin is getting a page for uploading files meant for public use. They belong in the public bucket, which so far only the public-upload key could write to. Grant asxpio's existing key put and delete there instead of handing the app a second set of credentials. Write-only is enough: the app keeps its own index of what it uploaded and never reads the objects back. Assisted-by: Claude:fable-5-2
105 lines
3.6 KiB
Bash
105 lines
3.6 KiB
Bash
#!/bin/sh
|
|
# Idempotent Garage bootstrap, run on the host after `docker compose up`:
|
|
# buckets, per-app keys and what each key may do. The Garage image has no
|
|
# shell, so this drives its CLI through `docker exec`.
|
|
#
|
|
# Env: ASXPIO_S3_ACCESS_KEY/_SECRET_KEY (required), BINGO_S3_* and
|
|
# PUBLIC_S3_* (optional). GARAGE_CONTAINER overrides the container name.
|
|
set -eu
|
|
|
|
c="${GARAGE_CONTAINER:-storage-garage}"
|
|
g() { docker exec "$c" /garage "$@"; }
|
|
|
|
# Garage only takes its own key format: an id of GK + 24 hex digits and a
|
|
# secret of 64 hex digits. A value already in that format is used as it is;
|
|
# anything else (the MinIO-era secrets) is hashed into it, the same way the
|
|
# apps' deploy workflows do, so the secrets did not have to change with the
|
|
# move. Replace a secret with a native Garage pair whenever it is rotated.
|
|
garage_key_id() {
|
|
if printf '%s' "$1" | grep -Eq '^GK[0-9a-f]{24}$'; then
|
|
printf '%s' "$1"
|
|
else
|
|
printf 'GK%s' "$(printf '%s' "$1" | sha256sum | cut -c1-24)"
|
|
fi
|
|
}
|
|
garage_secret() {
|
|
if printf '%s' "$1" | grep -Eq '^[0-9a-f]{64}$'; then
|
|
printf '%s' "$1"
|
|
else
|
|
printf '%s' "$1" | sha256sum | cut -c1-64
|
|
fi
|
|
}
|
|
|
|
i=0
|
|
until g status >/dev/null 2>&1; do
|
|
i=$((i + 1))
|
|
if [ "$i" -gt 60 ]; then
|
|
echo "garage-init: garage did not come up" >&2
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
ensure_bucket() {
|
|
g bucket info "$1" >/dev/null 2>&1 || g bucket create "$1" >/dev/null
|
|
}
|
|
|
|
# $1 name, $2 access key, $3 secret key. Prints the Garage key id.
|
|
ensure_key() {
|
|
id="$(garage_key_id "$2")"
|
|
secret="$(garage_secret "$3")"
|
|
if g key info --show-secret "$id" >/dev/null 2>&1; then
|
|
if ! g key info --show-secret "$id" | grep -q "$secret"; then
|
|
# Rotated secret under the same id.
|
|
g key delete --yes "$id" >/dev/null
|
|
g key import --yes -n "$1" "$id" "$secret" >/dev/null
|
|
fi
|
|
else
|
|
g key import --yes -n "$1" "$id" "$secret" >/dev/null
|
|
fi
|
|
printf '%s' "$id"
|
|
}
|
|
|
|
# $1 bucket, $2 key id, $3 "rw", "ro" or "wo". Sets exactly that.
|
|
grant() {
|
|
case "$3" in
|
|
rw) g bucket allow --read --write "$1" --key "$2" >/dev/null
|
|
g bucket deny --owner "$1" --key "$2" >/dev/null ;;
|
|
ro) g bucket allow --read "$1" --key "$2" >/dev/null
|
|
g bucket deny --write --owner "$1" --key "$2" >/dev/null ;;
|
|
wo) g bucket allow --write "$1" --key "$2" >/dev/null
|
|
g bucket deny --read --owner "$1" --key "$2" >/dev/null ;;
|
|
esac
|
|
}
|
|
|
|
ensure_bucket "asxpio-invoices"
|
|
# Datacenter bingo photos: uploaded and resized by asxpio's admin, served by
|
|
# datacenterbingo.
|
|
ensure_bucket "datacenterbingo-photos"
|
|
# Files shared by link: https://s3.asxp.io/public/<key>. Everything in this
|
|
# bucket is readable by anyone who knows the key; nobody can list it.
|
|
ensure_bucket "public"
|
|
g bucket website --allow public >/dev/null
|
|
|
|
asxpio="$(ensure_key asxpio "${ASXPIO_S3_ACCESS_KEY}" "${ASXPIO_S3_SECRET_KEY}")"
|
|
grant "asxpio-invoices" "$asxpio" rw
|
|
grant "datacenterbingo-photos" "$asxpio" rw
|
|
# asxpio's admin publishes and retracts files; it keeps its own index.
|
|
grant "public" "$asxpio" wo
|
|
|
|
if [ -n "${BINGO_S3_ACCESS_KEY:-}" ] && [ -n "${BINGO_S3_SECRET_KEY:-}" ]; then
|
|
bingo="$(ensure_key bingo "${BINGO_S3_ACCESS_KEY}" "${BINGO_S3_SECRET_KEY}")"
|
|
grant "datacenterbingo-photos" "$bingo" ro
|
|
else
|
|
echo "garage-init: BINGO_S3_* unset, skipping bingo key"
|
|
fi
|
|
|
|
# Put and delete only: the holder can publish and retract, not read or list.
|
|
if [ -n "${PUBLIC_S3_ACCESS_KEY:-}" ] && [ -n "${PUBLIC_S3_SECRET_KEY:-}" ]; then
|
|
uploader="$(ensure_key public-upload "${PUBLIC_S3_ACCESS_KEY}" "${PUBLIC_S3_SECRET_KEY}")"
|
|
grant "public" "$uploader" wo
|
|
else
|
|
echo "garage-init: PUBLIC_S3_* unset, skipping public uploader"
|
|
fi
|
|
|
|
echo "garage-init: done"
|