From f99f74f4a16e563f714e7a27ced6f7f1a195ad98 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Thu, 24 Sep 2026 17:05:23 +0200 Subject: [PATCH] Use Musify's own credentials on the shared services Infrastructure now gives each project its own Postgres role, RabbitMQ user and vhost, S3 identity limited to its bucket and a Zitadel service account that owns only its project, instead of handing out the admin accounts. - Postgres, RabbitMQ and S3 settings default to Musify's own (musify). - MassTransit connects to the `musify` vhost (RABBITMQ_VHOST, taken from the address path); RABBITMQ_DEFAULT_USER/PASS become RABBITMQ_USER and RABBITMQ_PASSWORD. - zitadel-init mounts ZITADEL_PAT_DIR (Infrastructure's zitadel/.output/musify) and uses that PAT. It no longer creates the Zitadel project, which that account can't do: Infrastructure does. --- deploy/.env.example | 36 +++++++++++++++++++++--------------- deploy/.env.prod.example | 15 ++++++++++----- deploy/compose.yml | 29 +++++++++++++++-------------- 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/deploy/.env.example b/deploy/.env.example index 160bfff..bf00cf6 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -5,8 +5,10 @@ # # Requires ../../../Infrastructure running first (Postgres, Zitadel, # SeaweedFS, RabbitMQ, Jaeger). See ../../../Infrastructure/README.md. -# Everything marked "must match Infrastructure's .env" below has to be the -# same value as the one in that file. +# Musify has its own credentials on every shared service (role, vhost, S3 +# identity, Zitadel service account), provisioned by Infrastructure. Every +# value marked "= Infrastructure's MUSIFY_*" below has to be the same as that +# setting in Infrastructure's .env. DOTNET_ENVIRONMENT=Development @@ -21,17 +23,18 @@ PUBLIC_STREAM_URL=http://localhost:8081 PUBLIC_AUTH_URL=http://host.docker.internal:8080 # --- PostgreSQL (Infrastructure's shared instance, Musify's own database) --- -# POSTGRES_USER / POSTGRES_PASSWORD must match Infrastructure's .env. +# Musify's own role, owner of musify_db only. POSTGRES_HOST=host.docker.internal -POSTGRES_USER=postgres -POSTGRES_PASSWORD=postgres +POSTGRES_USER=musify +# = Infrastructure's MUSIFY_DB_PASSWORD +POSTGRES_PASSWORD=musify POSTGRES_DB=musify_db # --- Zitadel (OIDC, shared instance) ----------------------------------------- -# Path to the admin service-account PAT Infrastructure's Zitadel writes on -# init. Adjust if your Infrastructure checkout lives somewhere else relative -# to this one. -ZITADEL_ADMIN_PAT_DIR=../../../Infrastructure/zitadel/.output +# Folder with Musify's own Zitadel PAT (a service account that owns the +# Musify project only), written by Infrastructure's `provision`. Adjust if +# your Infrastructure checkout lives somewhere else relative to this one. +ZITADEL_PAT_DIR=../../../Infrastructure/zitadel/.output/musify # Allows the OIDC apps this project creates to use plain-http redirect URIs # (dev only). ZITADEL_DEV_MODE=true @@ -43,16 +46,19 @@ WEB_CLIENT_ID= WEB_CLIENT_SECRET= # --- RabbitMQ (shared instance, MassTransit) -------------------------------- -# Must match Infrastructure's .env. +# Musify's own user, only on its own vhost. RABBITMQ_HOST=host.docker.internal -RABBITMQ_DEFAULT_USER=guest -RABBITMQ_DEFAULT_PASS=guest +RABBITMQ_VHOST=musify +RABBITMQ_USER=musify +# = Infrastructure's MUSIFY_RABBITMQ_PASSWORD +RABBITMQ_PASSWORD=musify # --- SeaweedFS (S3, shared instance, Musify's own bucket) -------------------- -# S3_ACCESS_KEY / S3_SECRET_KEY must match Infrastructure's .env. +# Musify's own identity, limited to S3_BUCKET. SEAWEEDFS_HOST=host.docker.internal -S3_ACCESS_KEY=admin_access_key -S3_SECRET_KEY=admin_secret_key +# = Infrastructure's MUSIFY_S3_ACCESS_KEY / MUSIFY_S3_SECRET_KEY +S3_ACCESS_KEY=musify_access_key +S3_SECRET_KEY=musify_secret_key S3_BUCKET=webapi-storage S3_PUBLIC_URL=http://host.docker.internal:8333 S3_INTERNAL_URL=http://host.docker.internal:8333 diff --git a/deploy/.env.prod.example b/deploy/.env.prod.example index 4b69699..d87ed45 100644 --- a/deploy/.env.prod.example +++ b/deploy/.env.prod.example @@ -28,14 +28,16 @@ PUBLIC_AUTH_URL=https://auth.example.com S3_PUBLIC_URL=https://s3.example.com # --- PostgreSQL (Infrastructure's shared instance, Musify's own database) --- -# POSTGRES_USER / POSTGRES_PASSWORD must match Infrastructure's .env.prod. +# Musify's own role, owner of musify_db only. POSTGRES_HOST=postgres -POSTGRES_USER=postgres +POSTGRES_USER=musify +# = Infrastructure's MUSIFY_DB_PASSWORD POSTGRES_PASSWORD=CHANGE_ME POSTGRES_DB=musify_db # --- Zitadel (OIDC, shared instance) ----------------------------------------- -ZITADEL_ADMIN_PAT_DIR=../../../Infrastructure/zitadel/.output +# Folder with Musify's own Zitadel PAT, written by Infrastructure's `provision`. +ZITADEL_PAT_DIR=../../../Infrastructure/zitadel/.output/musify ZITADEL_DEV_MODE=false # OIDC clients: the zitadel-init one-shot service (see compose.yml) fills @@ -46,11 +48,14 @@ WEB_CLIENT_SECRET= # --- RabbitMQ (shared instance, MassTransit) -------------------------------- RABBITMQ_HOST=rabbitmq -RABBITMQ_DEFAULT_USER=musify -RABBITMQ_DEFAULT_PASS=CHANGE_ME +RABBITMQ_VHOST=musify +RABBITMQ_USER=musify +# = Infrastructure's MUSIFY_RABBITMQ_PASSWORD +RABBITMQ_PASSWORD=CHANGE_ME # --- SeaweedFS (S3, shared instance, Musify's own bucket) -------------------- SEAWEEDFS_HOST=seaweedfs +# = Infrastructure's MUSIFY_S3_ACCESS_KEY / MUSIFY_S3_SECRET_KEY S3_ACCESS_KEY=CHANGE_ME S3_SECRET_KEY=CHANGE_ME S3_BUCKET=webapi-storage diff --git a/deploy/compose.yml b/deploy/compose.yml index 38df9fb..b163a8b 100644 --- a/deploy/compose.yml +++ b/deploy/compose.yml @@ -26,9 +26,10 @@ # already do. x-backend-env: &backend-env Database__ConnectionString: "Host=${POSTGRES_HOST};Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}" - MassTransit__Address: "rabbitmq://${RABBITMQ_HOST}:5672" - MassTransit__Username: ${RABBITMQ_DEFAULT_USER} - MassTransit__Password: ${RABBITMQ_DEFAULT_PASS} + # Musify's own vhost and user on the shared broker (the path is the vhost). + MassTransit__Address: "rabbitmq://${RABBITMQ_HOST}:5672/${RABBITMQ_VHOST}" + MassTransit__Username: ${RABBITMQ_USER} + MassTransit__Password: ${RABBITMQ_PASSWORD} # The public S3 URL, not the internal one: the same address is used to sign # the presigned upload/download URLs the browser has to be able to reach. InfrastructureStorage__Address: ${S3_PUBLIC_URL} @@ -135,7 +136,7 @@ services: PUBLIC_WEB_URL: ${PUBLIC_WEB_URL} ZITADEL_DEV_MODE: ${ZITADEL_DEV_MODE:-false} volumes: - - ${ZITADEL_ADMIN_PAT_DIR}:/output:ro + - ${ZITADEL_PAT_DIR}:/output:ro entrypoint: ["/bin/sh", "-c"] command: - | @@ -163,13 +164,15 @@ services: fi } - echo "==> waiting for the Zitadel service-account PAT" + # Musify's own service account, owner of the Musify project only + # (created by Infrastructure's `provision`, see its README). + echo "==> waiting for Musify's Zitadel PAT" deadline=$$(( $$(date +%s) + 180 )) - while [ ! -f /output/admin-sa.pat ]; do - [ "$$(date +%s)" -lt "$$deadline" ] || { echo "PAT not found under /output. Is Infrastructure's Zitadel running? Check 'docker logs infra-zitadel'." >&2; exit 1; } + while [ ! -s /output/zitadel.pat ]; do + [ "$$(date +%s)" -lt "$$deadline" ] || { echo "no zitadel.pat in ZITADEL_PAT_DIR. Is Infrastructure up, and does ZITADEL_PAT_DIR point at its zitadel/.output/musify? Check 'docker logs infra-provision'." >&2; exit 1; } sleep 3 done - PAT=$$(tr -d '\r\n' waiting for Zitadel to answer at $$PUBLIC_AUTH_URL" @@ -190,13 +193,11 @@ services: echo "==> project" PROJECT_NAME=Musify + # Created by Infrastructure's `provision`: this account can't create + # projects, only manage this one. PROJECT_ID=$$(api_call POST /projects/_search "$$(name_query "$$PROJECT_NAME")" | jq -r --arg n "$$PROJECT_NAME" '.result // [] | map(select(.name==$$n)) | first | .id // empty') - if [ -z "$$PROJECT_ID" ]; then - PROJECT_ID=$$(api_call POST /projects "$$(printf '{"name":"%s"}' "$$PROJECT_NAME")" | jq -r '.id') - echo " created (id $$PROJECT_ID)" - else - echo " reused (id $$PROJECT_ID)" - fi + [ -n "$$PROJECT_ID" ] || { echo "Zitadel project $$PROJECT_NAME not found. Is musify in Infrastructure's x-projects?" >&2; exit 1; } + echo " found (id $$PROJECT_ID)" find_app() { api_call POST "/projects/$$PROJECT_ID/apps/_search" "$$(name_query "$$1")" | jq -r --arg n "$$1" '.result // [] | map(select(.name==$$n)) | first | .id // empty'; }