From fdd8f29af2c9237122594c6d55334a6e0736ec9f Mon Sep 17 00:00:00 2001 From: Ife Ayelabola Date: Wed, 15 Jul 2026 09:25:05 +0100 Subject: [PATCH 1/4] adding changes for checking for SAS keys, this should only apply in review environment --- ...DR-005_Managed_Identity_For_Azure_Relay.md | 7 ++-- docs/relay-listener/README.md | 1 + infrastructure/modules/arc-infra/outputs.tf | 4 +-- infrastructure/modules/arc-infra/relay.tf | 2 +- scripts/bash/deploy_arc_ring.sh | 35 +++++++++++-------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/docs/adr/ADR-005_Managed_Identity_For_Azure_Relay.md b/docs/adr/ADR-005_Managed_Identity_For_Azure_Relay.md index 8fd1181a..9db9cb08 100644 --- a/docs/adr/ADR-005_Managed_Identity_For_Azure_Relay.md +++ b/docs/adr/ADR-005_Managed_Identity_For_Azure_Relay.md @@ -22,10 +22,11 @@ That said, setting up a working Relay connection locally is already complex. Man In **production** (`ENVIRONMENT=prod`), the gateway uses `ManagedIdentityCredential` exclusively. The SAS token path is unavailable regardless of what environment variables are set. The gateway's managed identity must be assigned the **Azure Relay Listener** role on the hybrid connection resource in Azure. -In **non-production** environments, the auth method is determined by whether `AZURE_RELAY_SHARED_ACCESS_KEY` is set: +In **non-production** environments, SAS-based authentication is only enabled in the review environment -- If set, a SAS token is generated and embedded in the WebSocket URL (`sb-hc-token`), preserving the simpler local development setup. -- If absent, `DefaultAzureCredential` is used, which works with Azure CLI credentials (`az login`) for developers who have the Listener role assigned to their identity. +- In the review environment, a SAS key may be provisioned and injected into `.env` so local testing against the gateway test VM continues to work. +- In dev, preprod, and prod the relay listener uses managed identity / Azure AD authentication and no SAS key is provisioned or injected into `.env`. +- If no SAS key is present, `DefaultAzureCredential` is used for developer scenarios that rely on `az login` and the appropriate Relay Listener role assignment. The token is passed as an `Authorization: Bearer` HTTP header on the WebSocket upgrade request for managed identity paths. Azure Relay validates it against Azure AD. diff --git a/docs/relay-listener/README.md b/docs/relay-listener/README.md index 2be32437..b0e2c9ce 100644 --- a/docs/relay-listener/README.md +++ b/docs/relay-listener/README.md @@ -3,6 +3,7 @@ Relay listener uses websocket communication to Manage Breast Screening service via Azure Relay. The listener processes worklist actions sent from Manage/Django and creates worklist items in the Modality Worklist server. +Note: SAS-based relay authentication is review-only. For dev, preprod, and prod, the listener should use managed identity and no `AZURE_RELAY_SHARED_ACCESS_KEY` should be injected into `.env`. ## Architecture diff --git a/infrastructure/modules/arc-infra/outputs.tf b/infrastructure/modules/arc-infra/outputs.tf index 5030ce4f..7e4e6b92 100644 --- a/infrastructure/modules/arc-infra/outputs.tf +++ b/infrastructure/modules/arc-infra/outputs.tf @@ -35,9 +35,9 @@ output "app_insights_connection_string" { } output "relay_listen_sas_keys" { - description = "Per-machine relay listen SAS primary keys, keyed by Arc resource name. Used by the deploy pipeline to write .env files." + description = "Per-machine relay listen SAS primary keys, keyed by Arc resource name. Populated only for the review environment." sensitive = true - value = var.enable_arc_servers ? { + value = var.enable_arc_servers && var.env_config == "review" ? { for k, rule in azurerm_relay_hybrid_connection_authorization_rule.per_machine_listen : k => rule.primary_key } : {} diff --git a/infrastructure/modules/arc-infra/relay.tf b/infrastructure/modules/arc-infra/relay.tf index db8a945f..a31a8e22 100644 --- a/infrastructure/modules/arc-infra/relay.tf +++ b/infrastructure/modules/arc-infra/relay.tf @@ -48,7 +48,7 @@ resource "azurerm_relay_hybrid_connection" "per_machine" { # Listen-only SAS rule per HC — retained for local development / break-glass access. # Production relay authentication uses Managed Identity (see relay_listener_role below). resource "azurerm_relay_hybrid_connection_authorization_rule" "per_machine_listen" { - for_each = local.arc_machines + for_each = var.env_config == "review" ? local.arc_machines : {} name = "listen" hybrid_connection_name = azurerm_relay_hybrid_connection.per_machine[each.key].name diff --git a/scripts/bash/deploy_arc_ring.sh b/scripts/bash/deploy_arc_ring.sh index 0b7612a6..b95909f5 100755 --- a/scripts/bash/deploy_arc_ring.sh +++ b/scripts/bash/deploy_arc_ring.sh @@ -73,24 +73,31 @@ while IFS= read -r MACHINE_JSON; do LOCATION=$(echo "$MACHINE_JSON" | jq -r '.location') echo "Preparing deploy for $MACHINE ($LOCATION)..." +RELAY_AUTH_BLOCK="" +if [[ "${ENV_CONFIG}" == "review" && -n "${AZURE_RELAY_SHARED_ACCESS_KEY:-}" ]]; then + RELAY_AUTH_BLOCK="AZURE_RELAY_KEY_NAME=listen +AZURE_RELAY_SHARED_ACCESS_KEY=${AZURE_RELAY_SHARED_ACCESS_KEY} +" +fi + # Build .env, then base64-encode to pass newlines as a run command parameter. # NOTE: Arc Run Command drops protectedParameters for inline source.script, # so EnvContentB64 travels as a regular parameter (base64-encoded, not plain text). ENV_CONTENT="AZURE_RELAY_NAMESPACE=${RELAY_NAMESPACE_HOSTNAME} -AZURE_RELAY_HYBRID_CONNECTION=hc-${MACHINE} -CLOUD_API_ENDPOINT=${CLOUD_API_ENDPOINT} -CLOUD_API_RESOURCE=${CLOUD_API_RESOURCE} -APPLICATIONINSIGHTS_CONNECTION_STRING=${APPLICATIONINSIGHTS_CONNECTION_STRING} -ENVIRONMENT=${ENVIRONMENT} -MWL_AET=RUBIE_MWL_${API_ENVIRONMENT} -MWL_PORT=104 -MWL_DB_PATH=${BASE_PATH}/data/worklist.db -PACS_AET=RUBIE_PACS_${API_ENVIRONMENT} -PACS_PORT=11112 -PACS_STORAGE_PATH=${BASE_PATH}/data/storage -PACS_DB_PATH=${BASE_PATH}/data/pacs.db -LOG_LEVEL=INFO -SAMPLE_IMAGES_PATH=${BASE_PATH}/current/sample_images" + AZURE_RELAY_HYBRID_CONNECTION=hc-${MACHINE} + CLOUD_API_ENDPOINT=${CLOUD_API_ENDPOINT} + CLOUD_API_RESOURCE=${CLOUD_API_RESOURCE} + APPLICATIONINSIGHTS_CONNECTION_STRING=${APPLICATIONINSIGHTS_CONNECTION_STRING} + ENVIRONMENT=${ENVIRONMENT} + MWL_AET=RUBIE_MWL_${API_ENVIRONMENT} + MWL_PORT=104 + MWL_DB_PATH=${BASE_PATH}/data/worklist.db + PACS_AET=RUBIE_PACS_${API_ENVIRONMENT} + PACS_PORT=11112 + PACS_STORAGE_PATH=${BASE_PATH}/data/storage + PACS_DB_PATH=${BASE_PATH}/data/pacs.db + LOG_LEVEL=INFO + ${RELAY_AUTH_BLOCK}SAMPLE_IMAGES_PATH=${BASE_PATH}/current/sample_images" # Cross-platform base64 encoding (works on macOS and Linux) ENV_CONTENT_B64=$(printf '%s' "$ENV_CONTENT" | base64 | tr -d '\n') From 0de5e31d43d3b27e638226994a3f666651ba7b80 Mon Sep 17 00:00:00 2001 From: Ife Ayelabola Date: Wed, 22 Jul 2026 09:52:19 +0100 Subject: [PATCH 2/4] fixed indentation --- scripts/bash/deploy_arc_ring.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/bash/deploy_arc_ring.sh b/scripts/bash/deploy_arc_ring.sh index b95909f5..0c20d7a4 100755 --- a/scripts/bash/deploy_arc_ring.sh +++ b/scripts/bash/deploy_arc_ring.sh @@ -84,20 +84,20 @@ fi # NOTE: Arc Run Command drops protectedParameters for inline source.script, # so EnvContentB64 travels as a regular parameter (base64-encoded, not plain text). ENV_CONTENT="AZURE_RELAY_NAMESPACE=${RELAY_NAMESPACE_HOSTNAME} - AZURE_RELAY_HYBRID_CONNECTION=hc-${MACHINE} - CLOUD_API_ENDPOINT=${CLOUD_API_ENDPOINT} - CLOUD_API_RESOURCE=${CLOUD_API_RESOURCE} - APPLICATIONINSIGHTS_CONNECTION_STRING=${APPLICATIONINSIGHTS_CONNECTION_STRING} - ENVIRONMENT=${ENVIRONMENT} - MWL_AET=RUBIE_MWL_${API_ENVIRONMENT} - MWL_PORT=104 - MWL_DB_PATH=${BASE_PATH}/data/worklist.db - PACS_AET=RUBIE_PACS_${API_ENVIRONMENT} - PACS_PORT=11112 - PACS_STORAGE_PATH=${BASE_PATH}/data/storage - PACS_DB_PATH=${BASE_PATH}/data/pacs.db - LOG_LEVEL=INFO - ${RELAY_AUTH_BLOCK}SAMPLE_IMAGES_PATH=${BASE_PATH}/current/sample_images" +AZURE_RELAY_HYBRID_CONNECTION=hc-${MACHINE} +CLOUD_API_ENDPOINT=${CLOUD_API_ENDPOINT} +CLOUD_API_RESOURCE=${CLOUD_API_RESOURCE} +APPLICATIONINSIGHTS_CONNECTION_STRING=${APPLICATIONINSIGHTS_CONNECTION_STRING} +ENVIRONMENT=${ENVIRONMENT} +MWL_AET=RUBIE_MWL_${API_ENVIRONMENT} +MWL_PORT=104 +MWL_DB_PATH=${BASE_PATH}/data/worklist.db +PACS_AET=RUBIE_PACS_${API_ENVIRONMENT} +PACS_PORT=11112 +PACS_STORAGE_PATH=${BASE_PATH}/data/storage +PACS_DB_PATH=${BASE_PATH}/data/pacs.db +LOG_LEVEL=INFO +${RELAY_AUTH_BLOCK}SAMPLE_IMAGES_PATH=${BASE_PATH}/current/sample_images" # Cross-platform base64 encoding (works on macOS and Linux) ENV_CONTENT_B64=$(printf '%s' "$ENV_CONTENT" | base64 | tr -d '\n') From 28ae5938256cb575605cfadc4e14d14cbc2adbbc Mon Sep 17 00:00:00 2001 From: Ife Ayelabola Date: Wed, 22 Jul 2026 10:01:19 +0100 Subject: [PATCH 3/4] build ENV_CONTENT first, then append RELAY_AUTH_BLOCK only when present, keeps the .env block cleaner and avoids inserting optional auth vars into the main env list, preserves alphabetical / logical ordering of the env vars in the generated content --- scripts/bash/deploy_arc_ring.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/bash/deploy_arc_ring.sh b/scripts/bash/deploy_arc_ring.sh index 0c20d7a4..5cfc07c0 100755 --- a/scripts/bash/deploy_arc_ring.sh +++ b/scripts/bash/deploy_arc_ring.sh @@ -97,7 +97,11 @@ PACS_PORT=11112 PACS_STORAGE_PATH=${BASE_PATH}/data/storage PACS_DB_PATH=${BASE_PATH}/data/pacs.db LOG_LEVEL=INFO -${RELAY_AUTH_BLOCK}SAMPLE_IMAGES_PATH=${BASE_PATH}/current/sample_images" +SAMPLE_IMAGES_PATH=${BASE_PATH}/current/sample_images" + + if [[ -n "${RELAY_AUTH_BLOCK}" ]]; then + ENV_CONTENT="${ENV_CONTENT}${RELAY_AUTH_BLOCK}" + fi # Cross-platform base64 encoding (works on macOS and Linux) ENV_CONTENT_B64=$(printf '%s' "$ENV_CONTENT" | base64 | tr -d '\n') From ddf3e1a014e707e70037f237c326e9845b47d98e Mon Sep 17 00:00:00 2001 From: Alastair Lock Date: Thu, 23 Jul 2026 09:44:47 +0100 Subject: [PATCH 4/4] wip --- infrastructure/modules/arc-infra/relay.tf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/infrastructure/modules/arc-infra/relay.tf b/infrastructure/modules/arc-infra/relay.tf index a31a8e22..41c77bac 100644 --- a/infrastructure/modules/arc-infra/relay.tf +++ b/infrastructure/modules/arc-infra/relay.tf @@ -48,7 +48,11 @@ resource "azurerm_relay_hybrid_connection" "per_machine" { # Listen-only SAS rule per HC — retained for local development / break-glass access. # Production relay authentication uses Managed Identity (see relay_listener_role below). resource "azurerm_relay_hybrid_connection_authorization_rule" "per_machine_listen" { - for_each = var.env_config == "review" ? local.arc_machines : {} + for_each = { + for machine_name, machine_config in local.arc_machines : + machine_name => machine_config + if var.env_config == "review" + } name = "listen" hybrid_connection_name = azurerm_relay_hybrid_connection.per_machine[each.key].name