There was no way to share a file by link: both buckets are private and the bootstrap forces anonymous access off on every run. Add a bucket named public whose objects anyone can GET by key, with no anonymous listing or writes, and an optional user that may only put and delete there, so a leaked uploader key reads nothing and reaches no other bucket. Assisted-by: Claude:fable-5-2
148 lines
4.3 KiB
Bash
148 lines
4.3 KiB
Bash
#!/bin/sh
|
|
# Idempotent MinIO bootstrap: bucket + per-app service account scoped to that
|
|
# bucket. Re-running just refreshes the policy and resets the access key.
|
|
set -eu
|
|
|
|
mc alias set local http://minio:9000 "${MINIO_ROOT_USER}" "${MINIO_ROOT_PASSWORD}"
|
|
|
|
ensure_bucket() {
|
|
bucket="$1"
|
|
if ! mc ls "local/${bucket}" >/dev/null 2>&1; then
|
|
mc mb "local/${bucket}"
|
|
fi
|
|
mc anonymous set none "local/${bucket}" >/dev/null
|
|
}
|
|
|
|
write_policy() {
|
|
name="$1"
|
|
bucket="$2"
|
|
cat > "/tmp/${name}.json" <<EOF
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": ["s3:ListBucket", "s3:GetBucketLocation"],
|
|
"Resource": ["arn:aws:s3:::${bucket}"]
|
|
},
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
|
|
"Resource": ["arn:aws:s3:::${bucket}/*"]
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
mc admin policy create local "${name}" "/tmp/${name}.json" 2>/dev/null \
|
|
|| mc admin policy update local "${name}" "/tmp/${name}.json"
|
|
}
|
|
|
|
# Read-only on one prefix of a bucket.
|
|
write_ro_prefix_policy() {
|
|
name="$1"
|
|
bucket="$2"
|
|
prefix="$3"
|
|
cat > "/tmp/${name}.json" <<EOF
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": ["s3:GetObject"],
|
|
"Resource": ["arn:aws:s3:::${bucket}/${prefix}*"]
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
mc admin policy create local "${name}" "/tmp/${name}.json" 2>/dev/null \
|
|
|| mc admin policy update local "${name}" "/tmp/${name}.json"
|
|
}
|
|
|
|
# World-readable by object key; no anonymous listing or writes.
|
|
ensure_public_bucket() {
|
|
bucket="$1"
|
|
if ! mc ls "local/${bucket}" >/dev/null 2>&1; then
|
|
mc mb "local/${bucket}"
|
|
fi
|
|
cat > "/tmp/${bucket}-anonymous.json" <<EOF
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Principal": {"AWS": ["*"]},
|
|
"Action": ["s3:GetObject"],
|
|
"Resource": ["arn:aws:s3:::${bucket}/*"]
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
mc anonymous set-json "/tmp/${bucket}-anonymous.json" "local/${bucket}" >/dev/null
|
|
}
|
|
|
|
# Put and delete only: the holder can publish and retract, not read or list.
|
|
write_upload_policy() {
|
|
name="$1"
|
|
bucket="$2"
|
|
cat > "/tmp/${name}.json" <<EOF
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": [
|
|
{
|
|
"Effect": "Allow",
|
|
"Action": ["s3:PutObject", "s3:DeleteObject"],
|
|
"Resource": ["arn:aws:s3:::${bucket}/*"]
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
mc admin policy create local "${name}" "/tmp/${name}.json" 2>/dev/null \
|
|
|| mc admin policy update local "${name}" "/tmp/${name}.json"
|
|
}
|
|
|
|
# $3: one or more policy names, space-separated.
|
|
ensure_user() {
|
|
access="$1"
|
|
secret="$2"
|
|
policies="$3"
|
|
if mc admin user info local "${access}" >/dev/null 2>&1; then
|
|
# Rotate secret to match desired state.
|
|
mc admin user remove local "${access}" >/dev/null
|
|
fi
|
|
mc admin user add local "${access}" "${secret}"
|
|
# shellcheck disable=SC2086 # word-split the policy list on purpose
|
|
mc admin policy attach local ${policies} --user "${access}" 2>/dev/null || true
|
|
}
|
|
|
|
ensure_bucket "asxpio-invoices"
|
|
write_policy "asxpio-invoices-rw" "asxpio-invoices"
|
|
|
|
# Datacenter bingo photos: uploaded and resized by asxpio's admin, served by
|
|
# datacenterbingo. Keys keep their bingo/ prefix, so DB rows stay valid.
|
|
ensure_bucket "datacenterbingo-photos"
|
|
write_policy "datacenterbingo-photos-rw" "datacenterbingo-photos"
|
|
write_ro_prefix_policy "datacenterbingo-photos-ro" "datacenterbingo-photos" ""
|
|
|
|
ensure_user "${ASXPIO_S3_ACCESS_KEY}" "${ASXPIO_S3_SECRET_KEY}" "asxpio-invoices-rw datacenterbingo-photos-rw"
|
|
|
|
if [ -n "${BINGO_S3_ACCESS_KEY:-}" ] && [ -n "${BINGO_S3_SECRET_KEY:-}" ]; then
|
|
ensure_user "${BINGO_S3_ACCESS_KEY}" "${BINGO_S3_SECRET_KEY}" "datacenterbingo-photos-ro"
|
|
else
|
|
echo "minio-init: BINGO_S3_* unset, skipping bingo user"
|
|
fi
|
|
|
|
# Files shared by link: https://s3.asxp.io/public/<key>. Everything in this
|
|
# bucket is readable by anyone who knows the key.
|
|
ensure_public_bucket "public"
|
|
write_upload_policy "public-upload" "public"
|
|
|
|
if [ -n "${PUBLIC_S3_ACCESS_KEY:-}" ] && [ -n "${PUBLIC_S3_SECRET_KEY:-}" ]; then
|
|
ensure_user "${PUBLIC_S3_ACCESS_KEY}" "${PUBLIC_S3_SECRET_KEY}" "public-upload"
|
|
else
|
|
echo "minio-init: PUBLIC_S3_* unset, skipping public uploader"
|
|
fi
|
|
|
|
# Leftover from before the photos had their own bucket.
|
|
mc admin policy remove local "asxpio-invoices-bingo-ro" >/dev/null 2>&1 || true
|
|
|
|
echo "minio-init: done"
|