Provision read-only credentials for datacenterbingo
All checks were successful
Deploy storage stack / deploy (push) Successful in 19s
All checks were successful
Deploy storage stack / deploy (push) Successful in 19s
The public bingo site is moving out of the asxpio container and only needs to read the bingo tables and the bingo/ photos. Give it a role limited to SELECT on those two tables (with read-only transactions by default) and a MinIO user limited to GetObject under asxpio-invoices/bingo/, instead of reusing asxpio's read-write credentials. Both steps are skipped while the BINGO_* secrets are unset, so this deploys safely before they exist. Tables missing on a fresh install are granted on the next deploy. Assisted-by: Claude:opus-5
This commit is contained in:
parent
459ee15f00
commit
b6bcc9f882
6 changed files with 86 additions and 1 deletions
|
|
@ -14,3 +14,7 @@ MINIO_ROOT_PASSWORD=change-me-32-chars-min
|
||||||
ASXPIO_DB_PASSWORD=change-me
|
ASXPIO_DB_PASSWORD=change-me
|
||||||
ASXPIO_S3_ACCESS_KEY=change-me
|
ASXPIO_S3_ACCESS_KEY=change-me
|
||||||
ASXPIO_S3_SECRET_KEY=change-me
|
ASXPIO_S3_SECRET_KEY=change-me
|
||||||
|
# datacenterbingo: read-only on asxpio's bingo tables and bingo/ photos.
|
||||||
|
BINGO_DB_PASSWORD=change-me
|
||||||
|
BINGO_S3_ACCESS_KEY=change-me
|
||||||
|
BINGO_S3_SECRET_KEY=change-me
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,14 @@ jobs:
|
||||||
ASXPIO_DB_PASSWORD: ${{ secrets.ASXPIO_DB_PASSWORD }}
|
ASXPIO_DB_PASSWORD: ${{ secrets.ASXPIO_DB_PASSWORD }}
|
||||||
ASXPIO_S3_ACCESS_KEY: ${{ secrets.ASXPIO_S3_ACCESS_KEY }}
|
ASXPIO_S3_ACCESS_KEY: ${{ secrets.ASXPIO_S3_ACCESS_KEY }}
|
||||||
ASXPIO_S3_SECRET_KEY: ${{ secrets.ASXPIO_S3_SECRET_KEY }}
|
ASXPIO_S3_SECRET_KEY: ${{ secrets.ASXPIO_S3_SECRET_KEY }}
|
||||||
|
BINGO_DB_PASSWORD: ${{ secrets.BINGO_DB_PASSWORD }}
|
||||||
|
BINGO_S3_ACCESS_KEY: ${{ secrets.BINGO_S3_ACCESS_KEY }}
|
||||||
|
BINGO_S3_SECRET_KEY: ${{ secrets.BINGO_S3_SECRET_KEY }}
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.DEPLOY_IP }}
|
host: ${{ secrets.DEPLOY_IP }}
|
||||||
username: ${{ secrets.DEPLOY_USER }}
|
username: ${{ secrets.DEPLOY_USER }}
|
||||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
envs: POSTGRES_PASSWORD,MINIO_ROOT_USER,MINIO_ROOT_PASSWORD,ASXPIO_DB_PASSWORD,ASXPIO_S3_ACCESS_KEY,ASXPIO_S3_SECRET_KEY
|
envs: POSTGRES_PASSWORD,MINIO_ROOT_USER,MINIO_ROOT_PASSWORD,ASXPIO_DB_PASSWORD,ASXPIO_S3_ACCESS_KEY,ASXPIO_S3_SECRET_KEY,BINGO_DB_PASSWORD,BINGO_S3_ACCESS_KEY,BINGO_S3_SECRET_KEY
|
||||||
script_stop: true
|
script_stop: true
|
||||||
script: |
|
script: |
|
||||||
umask 077
|
umask 077
|
||||||
|
|
@ -51,6 +54,9 @@ jobs:
|
||||||
ASXPIO_DB_PASSWORD=${ASXPIO_DB_PASSWORD}
|
ASXPIO_DB_PASSWORD=${ASXPIO_DB_PASSWORD}
|
||||||
ASXPIO_S3_ACCESS_KEY=${ASXPIO_S3_ACCESS_KEY}
|
ASXPIO_S3_ACCESS_KEY=${ASXPIO_S3_ACCESS_KEY}
|
||||||
ASXPIO_S3_SECRET_KEY=${ASXPIO_S3_SECRET_KEY}
|
ASXPIO_S3_SECRET_KEY=${ASXPIO_S3_SECRET_KEY}
|
||||||
|
BINGO_DB_PASSWORD=${BINGO_DB_PASSWORD}
|
||||||
|
BINGO_S3_ACCESS_KEY=${BINGO_S3_ACCESS_KEY}
|
||||||
|
BINGO_S3_SECRET_KEY=${BINGO_S3_SECRET_KEY}
|
||||||
EOF
|
EOF
|
||||||
chmod 600 /opt/storage/.env
|
chmod 600 /opt/storage/.env
|
||||||
chmod +x /opt/storage/init/postgres-init.sh /opt/storage/init/minio-bootstrap.sh
|
chmod +x /opt/storage/init/postgres-init.sh /opt/storage/init/minio-bootstrap.sh
|
||||||
|
|
|
||||||
18
SECRETS.md
18
SECRETS.md
|
|
@ -59,6 +59,24 @@ can connect.
|
||||||
| `ASXPIO_S3_ACCESS_KEY` | 20-char alphanumeric, see snippet above. |
|
| `ASXPIO_S3_ACCESS_KEY` | 20-char alphanumeric, see snippet above. |
|
||||||
| `ASXPIO_S3_SECRET_KEY` | 40-char alphanumeric, see snippet above. |
|
| `ASXPIO_S3_SECRET_KEY` | 40-char alphanumeric, see snippet above. |
|
||||||
|
|
||||||
|
### Per-app credentials provisioned for datacenterbingo
|
||||||
|
|
||||||
|
These are read-only:
|
||||||
|
- Role `bingo` gets SELECT on `bingo_datacenters` and `bingo_incidents` in
|
||||||
|
the `asxpio` DB, and `default_transaction_read_only`.
|
||||||
|
- The MinIO user may only `GetObject` under `asxpio-invoices/bingo/`.
|
||||||
|
|
||||||
|
The same values go on the **datacenterbingo repo**.
|
||||||
|
|
||||||
|
| Secret | Value |
|
||||||
|
|------------------------|--------------------------------------------------------|
|
||||||
|
| `BINGO_DB_PASSWORD` | Postgres password for the `bingo` role. `openssl rand -hex 32`. |
|
||||||
|
| `BINGO_S3_ACCESS_KEY` | 20-char alphanumeric. |
|
||||||
|
| `BINGO_S3_SECRET_KEY` | 40-char alphanumeric. |
|
||||||
|
|
||||||
|
The table grants need asxpio's migrations to have run. On a fresh install,
|
||||||
|
deploy storage again after asxpio's first boot.
|
||||||
|
|
||||||
## After adding secrets
|
## After adding secrets
|
||||||
|
|
||||||
1. Push to `main`. The Forgejo workflow brings Postgres and MinIO up, then runs
|
1. Push to `main`. The Forgejo workflow brings Postgres and MinIO up, then runs
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ services:
|
||||||
PGUSER: postgres
|
PGUSER: postgres
|
||||||
PGPASSWORD: ${POSTGRES_PASSWORD}
|
PGPASSWORD: ${POSTGRES_PASSWORD}
|
||||||
ASXPIO_DB_PASSWORD: ${ASXPIO_DB_PASSWORD}
|
ASXPIO_DB_PASSWORD: ${ASXPIO_DB_PASSWORD}
|
||||||
|
BINGO_DB_PASSWORD: ${BINGO_DB_PASSWORD}
|
||||||
volumes:
|
volumes:
|
||||||
- ./init/postgres-init.sh:/init.sh:ro
|
- ./init/postgres-init.sh:/init.sh:ro
|
||||||
entrypoint: ["/bin/sh", "/init.sh"]
|
entrypoint: ["/bin/sh", "/init.sh"]
|
||||||
|
|
@ -75,6 +76,8 @@ services:
|
||||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
ASXPIO_S3_ACCESS_KEY: ${ASXPIO_S3_ACCESS_KEY}
|
ASXPIO_S3_ACCESS_KEY: ${ASXPIO_S3_ACCESS_KEY}
|
||||||
ASXPIO_S3_SECRET_KEY: ${ASXPIO_S3_SECRET_KEY}
|
ASXPIO_S3_SECRET_KEY: ${ASXPIO_S3_SECRET_KEY}
|
||||||
|
BINGO_S3_ACCESS_KEY: ${BINGO_S3_ACCESS_KEY}
|
||||||
|
BINGO_S3_SECRET_KEY: ${BINGO_S3_SECRET_KEY}
|
||||||
volumes:
|
volumes:
|
||||||
- ./init/minio-bootstrap.sh:/bootstrap.sh:ro
|
- ./init/minio-bootstrap.sh:/bootstrap.sh:ro
|
||||||
entrypoint: ["/bin/sh", "/bootstrap.sh"]
|
entrypoint: ["/bin/sh", "/bootstrap.sh"]
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,27 @@ EOF
|
||||||
|| mc admin policy update local "${name}" "/tmp/${name}.json"
|
|| 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"
|
||||||
|
}
|
||||||
|
|
||||||
ensure_user() {
|
ensure_user() {
|
||||||
access="$1"
|
access="$1"
|
||||||
secret="$2"
|
secret="$2"
|
||||||
|
|
@ -53,4 +74,12 @@ ensure_bucket "asxpio-invoices"
|
||||||
write_policy "asxpio-invoices-rw" "asxpio-invoices"
|
write_policy "asxpio-invoices-rw" "asxpio-invoices"
|
||||||
ensure_user "${ASXPIO_S3_ACCESS_KEY}" "${ASXPIO_S3_SECRET_KEY}" "asxpio-invoices-rw"
|
ensure_user "${ASXPIO_S3_ACCESS_KEY}" "${ASXPIO_S3_SECRET_KEY}" "asxpio-invoices-rw"
|
||||||
|
|
||||||
|
# datacenterbingo serves the card photos asxpio's admin uploads under bingo/.
|
||||||
|
if [ -n "${BINGO_S3_ACCESS_KEY:-}" ] && [ -n "${BINGO_S3_SECRET_KEY:-}" ]; then
|
||||||
|
write_ro_prefix_policy "asxpio-invoices-bingo-ro" "asxpio-invoices" "bingo/"
|
||||||
|
ensure_user "${BINGO_S3_ACCESS_KEY}" "${BINGO_S3_SECRET_KEY}" "asxpio-invoices-bingo-ro"
|
||||||
|
else
|
||||||
|
echo "minio-init: BINGO_S3_* unset, skipping bingo user"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "minio-init: done"
|
echo "minio-init: done"
|
||||||
|
|
|
||||||
|
|
@ -33,4 +33,29 @@ ensure_role "asxpio" "${ASXPIO_DB_PASSWORD}"
|
||||||
ensure_db "asxpio" "asxpio"
|
ensure_db "asxpio" "asxpio"
|
||||||
ensure_extension "asxpio" "citext"
|
ensure_extension "asxpio" "citext"
|
||||||
|
|
||||||
|
# datacenterbingo: read-only on the bingo tables in the asxpio DB (asxpio's
|
||||||
|
# admin writes them and owns the schema). Tables that don't exist yet (fresh
|
||||||
|
# install, asxpio not migrated) are granted on the next storage deploy.
|
||||||
|
if [ -n "${BINGO_DB_PASSWORD:-}" ]; then
|
||||||
|
ensure_role "bingo" "${BINGO_DB_PASSWORD}"
|
||||||
|
psql -c "ALTER ROLE bingo SET default_transaction_read_only = on;"
|
||||||
|
psql -d asxpio -v ON_ERROR_STOP=1 <<'SQL' # heredoc body stays at column 0
|
||||||
|
GRANT CONNECT ON DATABASE asxpio TO bingo;
|
||||||
|
GRANT USAGE ON SCHEMA public TO bingo;
|
||||||
|
DO $$
|
||||||
|
DECLARE t text;
|
||||||
|
BEGIN
|
||||||
|
FOREACH t IN ARRAY ARRAY['bingo_datacenters', 'bingo_incidents'] LOOP
|
||||||
|
IF to_regclass('public.' || t) IS NOT NULL THEN
|
||||||
|
EXECUTE format('GRANT SELECT ON public.%I TO bingo', t);
|
||||||
|
ELSE
|
||||||
|
RAISE NOTICE 'bingo: % missing, not granted yet', t;
|
||||||
|
END IF;
|
||||||
|
END LOOP;
|
||||||
|
END $$;
|
||||||
|
SQL
|
||||||
|
else
|
||||||
|
echo "postgres-init: BINGO_DB_PASSWORD unset, skipping bingo role"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "postgres-init: done"
|
echo "postgres-init: done"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue