From f8a5c20aaffbe985fceed09ee92b133eac8f4a42 Mon Sep 17 00:00:00 2001 From: andrea rota Date: Tue, 19 Mar 2024 18:51:26 +0000 Subject: [PATCH] use vars rather than secrets where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plaintext variables were only introduced after the initial development of Marxan’s CI workflows: https://github.blog/changelog/2023-01-10-github-actions-support-for-configuration-variables-in-workflows/ Keeping only true secret values as GitHub Actions secrets allows better inspectability of what is configured in terms of CI keys/values. --- .github/workflows/deploy-to-kubernetes.yml | 6 +- .github/workflows/e2e-client.yml | 2 +- .../publish-marxan-docker-images.yml | 42 ++++----- .../publish-webshot-docker-images.yml | 10 +-- infrastructure/README.md | 34 +++++-- .../base/modules/github_secrets/main.tf | 90 +++++++++---------- 6 files changed, 100 insertions(+), 84 deletions(-) diff --git a/.github/workflows/deploy-to-kubernetes.yml b/.github/workflows/deploy-to-kubernetes.yml index fdecbd8822..5664351664 100644 --- a/.github/workflows/deploy-to-kubernetes.yml +++ b/.github/workflows/deploy-to-kubernetes.yml @@ -78,7 +78,7 @@ jobs: - name: Add custom host data run: | - sudo sh -c 'echo "127.0.0.1 ${{ secrets.AZURE_AKS_HOST }}" >> /etc/hosts' + sudo sh -c 'echo "127.0.0.1 ${{ env.AZURE_AKS_HOST }}" >> /etc/hosts' - name: Install kubectl uses: azure/setup-kubectl@v3 @@ -88,12 +88,12 @@ jobs: - name: Config kubectl run: | mkdir ~/.kube - az aks get-credentials --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} --name ${{ secrets.AZURE_AKS_CLUSTER_NAME }} + az aks get-credentials --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --name ${{ env.AZURE_AKS_CLUSTER_NAME }} sed -i 's/\([[:alnum:]]\+\?.privatelink.[[:alnum:]]\+\?.azmk8s.io\):443/\1:4433/g' ~/.kube/config - name: Creating SSH tunnel run: | - ssh -i ~/.ssh/bastion.key -o StrictHostKeyChecking=no -N -L 4433:${{ secrets.AZURE_AKS_HOST }}:443 ${{ secrets.BASTION_USER }}@${{ secrets.BASTION_HOST }} -T & + ssh -i ~/.ssh/bastion.key -o StrictHostKeyChecking=no -N -L 4433:${{ env.AZURE_AKS_HOST }}:443 ${{ env.BASTION_USER }}@${{ env.BASTION_HOST }} -T & - name: Redeploy production pods if: ${{ github.ref == 'refs/heads/main' }} diff --git a/.github/workflows/e2e-client.yml b/.github/workflows/e2e-client.yml index f0b0bde8b9..618642942d 100644 --- a/.github/workflows/e2e-client.yml +++ b/.github/workflows/e2e-client.yml @@ -41,7 +41,7 @@ jobs: path: playwright-report/ retention-days: 30 env: - NEXT_PUBLIC_MAPBOX_API_TOKEN: ${{ secrets.NEXT_PUBLIC_MAPBOX_API_TOKEN }} + NEXT_PUBLIC_MAPBOX_API_TOKEN: ${{ env.NEXT_PUBLIC_MAPBOX_API_TOKEN }} # Recommended: pass the GitHub token lets this action correctly # determine the unique run id necessary to re-run the checks GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-marxan-docker-images.yml b/.github/workflows/publish-marxan-docker-images.yml index 339a2365a4..e89e2eedbb 100644 --- a/.github/workflows/publish-marxan-docker-images.yml +++ b/.github/workflows/publish-marxan-docker-images.yml @@ -67,15 +67,15 @@ jobs: - name: Build and push image uses: azure/docker-login@v1 with: - login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }} - username: ${{ secrets.REGISTRY_USERNAME }} + login-server: ${{ env.REGISTRY_LOGIN_SERVER }} + username: ${{ env.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - run: | docker build ./api -f api/api.Dockerfile \ - -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-api:${{ github.sha }} \ - -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-api:${{ github.ref != 'refs/heads/main' && 'staging' || 'production' }} - docker push -a ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-api + -t ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-api:${{ github.sha }} \ + -t ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-api:${{ github.ref != 'refs/heads/main' && 'staging' || 'production' }} + docker push -a ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-api push_geoprocessing_to_registry: name: Push Geoprocessing Docker image to Azure Container Registry @@ -95,15 +95,15 @@ jobs: - name: Build and push image uses: azure/docker-login@v1 with: - login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }} - username: ${{ secrets.REGISTRY_USERNAME }} + login-server: ${{ env.REGISTRY_LOGIN_SERVER }} + username: ${{ env.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - run: | docker build ./api -f api/geo.Dockerfile \ - -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-geoprocessing:${{ github.sha }} \ - -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-geoprocessing:${{ github.ref != 'refs/heads/main' && 'staging' || 'production' }} - docker push -a ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-geoprocessing + -t ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-geoprocessing:${{ github.sha }} \ + -t ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-geoprocessing:${{ github.ref != 'refs/heads/main' && 'staging' || 'production' }} + docker push -a ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-geoprocessing push_client_to_registry: name: Push Client Docker image to Azure Container Registry @@ -124,19 +124,19 @@ jobs: - name: Build and push image uses: azure/docker-login@v1 with: - login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }} - username: ${{ secrets.REGISTRY_USERNAME }} + login-server: ${{ env.REGISTRY_LOGIN_SERVER }} + username: ${{ env.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - run: | docker build ./app \ - -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-client:${{ github.sha }} \ - -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-client:${{ github.ref != 'refs/heads/main' && 'staging' || 'production' }} \ - --build-arg NEXT_PUBLIC_URL=${{ github.ref != 'refs/heads/main' && secrets.NEXT_PUBLIC_URL_STAGING || secrets.NEXT_PUBLIC_URL_PRODUCTION }} \ - --build-arg NEXT_PUBLIC_API_URL=${{ github.ref != 'refs/heads/main' && secrets.NEXT_PUBLIC_API_URL_STAGING || secrets.NEXT_PUBLIC_API_URL_PRODUCTION }} \ - --build-arg NEXTAUTH_URL=${{ github.ref != 'refs/heads/main' && secrets.NEXTAUTH_URL_STAGING || secrets.NEXTAUTH_URL_PRODUCTION }} \ - --build-arg NEXT_PUBLIC_FEATURE_FLAGS=${{ github.ref != 'refs/heads/main' && secrets.NEXT_PUBLIC_FEATURE_FLAGS_STAGING || secrets.NEXT_PUBLIC_FEATURE_FLAGS_PRODUCTION }} \ - --build-arg NEXT_PUBLIC_MAPBOX_API_TOKEN=${{ secrets.NEXT_PUBLIC_MAPBOX_API_TOKEN }} \ + -t ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-client:${{ github.sha }} \ + -t ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-client:${{ github.ref != 'refs/heads/main' && 'staging' || 'production' }} \ + --build-arg NEXT_PUBLIC_URL=${{ github.ref != 'refs/heads/main' && env.NEXT_PUBLIC_URL_STAGING || env.NEXT_PUBLIC_URL_PRODUCTION }} \ + --build-arg NEXT_PUBLIC_API_URL=${{ github.ref != 'refs/heads/main' && env.NEXT_PUBLIC_API_URL_STAGING || env.NEXT_PUBLIC_API_URL_PRODUCTION }} \ + --build-arg NEXTAUTH_URL=${{ github.ref != 'refs/heads/main' && env.NEXTAUTH_URL_STAGING || env.NEXTAUTH_URL_PRODUCTION }} \ + --build-arg NEXT_PUBLIC_FEATURE_FLAGS=${{ github.ref != 'refs/heads/main' && env.NEXT_PUBLIC_FEATURE_FLAGS_STAGING || env.NEXT_PUBLIC_FEATURE_FLAGS_PRODUCTION }} \ + --build-arg NEXT_PUBLIC_MAPBOX_API_TOKEN=${{ env.NEXT_PUBLIC_MAPBOX_API_TOKEN }} \ --build-arg ENABLE_MAINTENANCE_MODE=${{ github.event.inputs.enable_maintenance_mode }} \ - --build-arg NEXT_PUBLIC_CONTACT_EMAIL=${{ secrets.NEXT_PUBLIC_CONTACT_EMAIL }} - docker push -a ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-client + --build-arg NEXT_PUBLIC_CONTACT_EMAIL=${{ env.NEXT_PUBLIC_CONTACT_EMAIL }} + docker push -a ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-client diff --git a/.github/workflows/publish-webshot-docker-images.yml b/.github/workflows/publish-webshot-docker-images.yml index e1be3a1a3b..c078523908 100644 --- a/.github/workflows/publish-webshot-docker-images.yml +++ b/.github/workflows/publish-webshot-docker-images.yml @@ -34,12 +34,12 @@ jobs: - name: Build and push image uses: azure/docker-login@v1 with: - login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }} - username: ${{ secrets.REGISTRY_USERNAME }} + login-server: ${{ env.REGISTRY_LOGIN_SERVER }} + username: ${{ env.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - run: | docker build ./webshot \ - -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-webshot:${{ github.sha }} \ - -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-webshot:${{ github.ref != 'refs/heads/main' && 'staging' || 'production' }} - docker push -a ${{ secrets.REGISTRY_LOGIN_SERVER }}/marxan-webshot + -t ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-webshot:${{ github.sha }} \ + -t ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-webshot:${{ github.ref != 'refs/heads/main' && 'staging' || 'production' }} + docker push -a ${{ env.REGISTRY_LOGIN_SERVER }}/marxan-webshot diff --git a/infrastructure/README.md b/infrastructure/README.md index 6fb2423f67..b277540917 100644 --- a/infrastructure/README.md +++ b/infrastructure/README.md @@ -97,23 +97,39 @@ the services on kubernetes, which is done by this plan. #### Github Actions -As part of this infrastructure, Github Actions are used to automatically build and push Docker images to Azure ACR, and -to redeploy Kubernetes pods once that happens. Said Github Actions depend on specific Github Secrets, that are listed below -for reference. Said secrets are automatically created by the `base` Terraform project, and do not need to be created manually. +As part of this infrastructure, Github Actions are used to automatically build +and push Docker images to Azure ACR, and to redeploy Kubernetes pods once that +happens. Said Github Actions depend on specific Github Secrets and Variables, +that are listed below for reference. + +Secrets and variables listed below are automatically created by the `base` +Terraform project, and do not need to be created manually. Their value often +depends on the outputs of other Terraform modules, so configuring all these via +Terraform (and avoiding to change them manually within the settings of the +relevant GitHub repository) guarantees that values available to GitHub actions +are always coherent with the state of the terraformed infrastructure. + +For example, AKS-related variables depend on settings for the cluster name as +well as the hostname of the AKS API server, which is assigned by Azure upon +creation of an AKS cluster. + +##### Secrets -- `AZURE_AKS_CLUSTER_NAME`: The name of the AKS cluster. Get from `Base`'s `k8s_cluster_name` -- `AZURE_AKS_HOST`: The AKS cluster hostname (without port or protocol). Get from `Base`'s `k8s_cluster_private_fqdn` - `AZURE_CLIENT_ID`: The hostname for the Azure ACT. Get from `Base`'s `container_registry_client_id` -- `AZURE_RESOURCE_GROUP`: The AKS Resource Group name. Specified by you when setting up the infrastructure. - `AZURE_SUBSCRIPTION_ID`: The Azure Subscription Id. Get from `Base`'s `azure_subscription_id` - `AZURE_TENANT_ID`: The Azure Tenant Id. Get from `Base`'s `azure_tenant_id` +- `BASTION_SSH_PRIVATE_KEY`: The ssh private key to access the bastion host. Get it by connection to the bastion host using SSH, and generating a new public/private SSH key pair. +- `REGISTRY_PASSWORD`: The password to access the Azure. Get from `Base`'s `container_registry_password` + +##### Variables + +- `AZURE_AKS_CLUSTER_NAME`: The name of the AKS cluster. Get from `Base`'s `k8s_cluster_name` +- `AZURE_AKS_HOST`: The AKS cluster hostname (without port or protocol). Get from `Base`'s `k8s_cluster_private_fqdn` +- `AZURE_RESOURCE_GROUP`: The AKS Resource Group name. Specified by you when setting up the infrastructure. - `BASTION_HOST`: The hostname for the bastion machine. Get from `Base`'s `bastion_hostname` - `BASTION_USER`: By default this will be `ubuntu` if using the initial user created on bastion host instantiation. It is configurable in case infrastructure admins wish to configure a different user on the bastion host or the default distro user is renamed. -- `BASTION_SSH_PRIVATE_KEY`: The ssh private key to access the bastion host. Get it by connection to the bastion host using SSH, and generating a new public/private SSH key pair. - `REGISTRY_LOGIN_SERVER`: The hostname for the Azure ACR. Get from `Base`'s `container_registry_hostname` - `REGISTRY_USERNAME`: The username for the Azure ACR. Get from `Base`'s `container_registry_client_id` -- `REGISTRY_PASSWORD`: The password to access the Azure. Get from `Base`'s `container_registry_password` -- `BASTION_SSH_PRIVATE_KEY`: The ssh private key to access the bastion host. Get it by connection to the bastion host using SSH, and generating a new public/private SSH key pair. Additional Github Actions Secrets are needed, as required by the [frontend application](../app/README.md#env-variables) and used by the corresponding [Github workflow](../.github/workflows/publish-marxan-docker-images.yml) that builds diff --git a/infrastructure/base/modules/github_secrets/main.tf b/infrastructure/base/modules/github_secrets/main.tf index a5587e93ea..534da7b29e 100644 --- a/infrastructure/base/modules/github_secrets/main.tf +++ b/infrastructure/base/modules/github_secrets/main.tf @@ -1,13 +1,13 @@ -resource "github_actions_secret" "azure_aks_cluster_name" { +resource "github_actions_variable" "azure_aks_cluster_name" { repository = var.repo_name - secret_name = "AZURE_AKS_CLUSTER_NAME" - plaintext_value = var.aks_cluster_name + variable_name = "AZURE_AKS_CLUSTER_NAME" + value = var.aks_cluster_name } -resource "github_actions_secret" "azure_aks_host" { +resource "github_actions_variable" "azure_aks_host" { repository = var.repo_name - secret_name = "AZURE_AKS_HOST" - plaintext_value = var.aks_host + variable_name = "AZURE_AKS_HOST" + value = var.aks_host } resource "github_actions_secret" "azure_client_id" { @@ -16,10 +16,10 @@ resource "github_actions_secret" "azure_client_id" { plaintext_value = var.client_id } -resource "github_actions_secret" "azure_resource_group" { +resource "github_actions_variable" "azure_resource_group" { repository = var.repo_name - secret_name = "AZURE_RESOURCE_GROUP" - plaintext_value = var.resource_group_name + variable_name = "AZURE_RESOURCE_GROUP" + value = var.resource_group_name } resource "github_actions_secret" "azure_subscription_id" { @@ -34,10 +34,10 @@ resource "github_actions_secret" "azure_tenant_id" { plaintext_value = var.tenant_id } -resource "github_actions_secret" "bastion_host" { +resource "github_actions_variable" "bastion_host" { repository = var.repo_name - secret_name = "BASTION_HOST" - plaintext_value = var.bastion_host + variable_name = "BASTION_HOST" + value = var.bastion_host } resource "github_actions_secret" "bastion_ssh_private_key" { @@ -46,16 +46,16 @@ resource "github_actions_secret" "bastion_ssh_private_key" { plaintext_value = var.bastion_ssh_private_key } -resource "github_actions_secret" "bastion_user" { +resource "github_actions_variable" "bastion_user" { repository = var.repo_name - secret_name = "BASTION_USER" - plaintext_value = var.bastion_user + variable_name = "BASTION_USER" + value = var.bastion_user } -resource "github_actions_secret" "registry_login_server" { +resource "github_actions_variable" "registry_login_server" { repository = var.repo_name - secret_name = "REGISTRY_LOGIN_SERVER" - plaintext_value = var.registry_login_server + variable_name = "REGISTRY_LOGIN_SERVER" + value = var.registry_login_server } resource "github_actions_secret" "registry_password" { @@ -64,56 +64,56 @@ resource "github_actions_secret" "registry_password" { plaintext_value = var.registry_password } -resource "github_actions_secret" "registry_username" { +resource "github_actions_variable" "registry_username" { repository = var.repo_name - secret_name = "REGISTRY_USERNAME" - plaintext_value = var.registry_username + variable_name = "REGISTRY_USERNAME" + value = var.registry_username } -resource "github_actions_secret" "mapbox_api_token" { +resource "github_actions_variable" "mapbox_api_token" { repository = var.repo_name - secret_name = "NEXT_PUBLIC_MAPBOX_API_TOKEN" - plaintext_value = var.mapbox_api_token + variable_name = "NEXT_PUBLIC_MAPBOX_API_TOKEN" + value = var.mapbox_api_token } -resource "github_actions_secret" "contact_email" { +resource "github_actions_variable" "contact_email" { repository = var.repo_name - secret_name = "NEXT_PUBLIC_CONTACT_EMAIL" - plaintext_value = var.support_email + variable_name = "NEXT_PUBLIC_CONTACT_EMAIL" + value = var.support_email } -resource "github_actions_secret" "next_public_api_url_production" { +resource "github_actions_variable" "next_public_api_url_production" { repository = var.repo_name - secret_name = "NEXT_PUBLIC_API_URL_PRODUCTION" - plaintext_value = "https://api.${var.domain}" + variable_name = "NEXT_PUBLIC_API_URL_PRODUCTION" + value = "https://api.${var.domain}" } -resource "github_actions_secret" "next_public_url_production" { +resource "github_actions_variable" "next_public_url_production" { repository = var.repo_name - secret_name = "NEXT_PUBLIC_URL_PRODUCTION" - plaintext_value = "https://${var.domain}" + variable_name = "NEXT_PUBLIC_URL_PRODUCTION" + value = "https://${var.domain}" } -resource "github_actions_secret" "nextauth_url_production" { +resource "github_actions_variable" "nextauth_url_production" { repository = var.repo_name - secret_name = "NEXTAUTH_URL_PRODUCTION" - plaintext_value = "https://client.${var.domain}" + variable_name = "NEXTAUTH_URL_PRODUCTION" + value = "https://client.${var.domain}" } -resource "github_actions_secret" "next_public_api_url_staging" { +resource "github_actions_variable" "next_public_api_url_staging" { repository = var.repo_name - secret_name = "NEXT_PUBLIC_API_URL_STAGING" - plaintext_value = "https://api.staging.${var.domain}" + variable_name = "NEXT_PUBLIC_API_URL_STAGING" + value = "https://api.staging.${var.domain}" } -resource "github_actions_secret" "next_public_url_staging" { +resource "github_actions_variable" "next_public_url_staging" { repository = var.repo_name - secret_name = "NEXT_PUBLIC_URL_STAGING" - plaintext_value = "https://staging.${var.domain}" + variable_name = "NEXT_PUBLIC_URL_STAGING" + value = "https://staging.${var.domain}" } -resource "github_actions_secret" "nextauth_url_staging" { +resource "github_actions_variable" "nextauth_url_staging" { repository = var.repo_name - secret_name = "NEXTAUTH_URL_STAGING" - plaintext_value = "https://client.staging.${var.domain}" + variable_name = "NEXTAUTH_URL_STAGING" + value = "https://client.staging.${var.domain}" }