diff --git a/.github/workflows/reusable-zizmor.yml b/.github/workflows/reusable-zizmor.yml index 12822383a..98c3bf135 100644 --- a/.github/workflows/reusable-zizmor.yml +++ b/.github/workflows/reusable-zizmor.yml @@ -355,9 +355,9 @@ jobs: echo "always-use-default-config is set. Using default config." fi - ZIZMOR_CONFIG="${{ runner.temp }}/zizmor.yml" + ZIZMOR_CONFIG_PATH="${{ runner.temp }}/zizmor.yml" if [ -n "${DEFAULT_ZIZMOR_CONFIG_DOWNLOADED}" ]; then - echo "zizmor-config=${ZIZMOR_CONFIG}" | tee -a "${GITHUB_OUTPUT}" + echo "zizmor-config=${ZIZMOR_CONFIG_PATH}" | tee -a "${GITHUB_OUTPUT}" fi - name: Setup UV @@ -376,7 +376,7 @@ jobs: - name: Run zizmor env: - ZIZMOR_CONFIG: ${{ steps.setup-config.outputs.zizmor-config }} + ZIZMOR_CONFIG_PATH: ${{ steps.setup-config.outputs.zizmor-config }} ZIZMOR_CACHE_DIR: ${{ runner.temp }}/.cache/zizmor shell: sh run: >- @@ -385,7 +385,7 @@ jobs: --min-severity "${MIN_SEVERITY}" --min-confidence "${MIN_CONFIDENCE}" --cache-dir "${ZIZMOR_CACHE_DIR}" - ${ZIZMOR_CONFIG:+--config "${ZIZMOR_CONFIG}"} + ${ZIZMOR_CONFIG_PATH:+--config "${ZIZMOR_CONFIG_PATH}"} ${RUNNER_DEBUG:+"--verbose"} ${ZIZMOR_EXTRA_ARGS:+${ZIZMOR_EXTRA_ARGS}} . @@ -410,7 +410,7 @@ jobs: id: zizmor-plain shell: bash env: - ZIZMOR_CONFIG: ${{ steps.setup-config.outputs.zizmor-config }} + ZIZMOR_CONFIG_PATH: ${{ steps.setup-config.outputs.zizmor-config }} ZIZMOR_CACHE_DIR: ${{ runner.temp }}/.cache/zizmor run: | set -o pipefail @@ -425,7 +425,7 @@ jobs: --min-confidence "${MIN_CONFIDENCE}" \ --cache-dir "${ZIZMOR_CACHE_DIR}" \ ${RUNNER_DEBUG:+"--verbose"} \ - ${ZIZMOR_CONFIG:+--config "${ZIZMOR_CONFIG}"} \ + ${ZIZMOR_CONFIG_PATH:+--config "${ZIZMOR_CONFIG_PATH}"} \ ${ZIZMOR_EXTRA_ARGS:+${ZIZMOR_EXTRA_ARGS}} \ . \ | tee -a "${GITHUB_OUTPUT}" @@ -443,9 +443,9 @@ jobs: - name: Remove zizmor config env: - ZIZMOR_CONFIG: ${{ steps.setup-config.outputs.zizmor-config }} + ZIZMOR_CONFIG_PATH: ${{ steps.setup-config.outputs.zizmor-config }} if: steps.setup-config.outputs.zizmor-config - run: rm "${ZIZMOR_CONFIG}" + run: rm "${ZIZMOR_CONFIG_PATH}" - name: Hide any previous comments if: ${{ !cancelled() && github.event.pull_request.head.repo.full_name == github.repository }} diff --git a/.github/workflows/test-get-vault-secrets.yaml b/.github/workflows/test-get-vault-secrets.yaml index 56ec00be5..fc005377b 100644 --- a/.github/workflows/test-get-vault-secrets.yaml +++ b/.github/workflows/test-get-vault-secrets.yaml @@ -67,10 +67,12 @@ jobs: - name: Check secret value is ${{ matrix.instance }} if: matrix.instance != 'invalid' run: | - if [[ "${{ env.INSTANCE }}" != "${{ matrix.instance }}" ]]; then + if [[ "${INSTANCE}" != "${{ matrix.instance }}" ]]; then echo "Test failed: secret value does not match vault_instance input" exit 1 fi + env: + INSTANCE: ${{ env.INSTANCE }} - name: Ensure 'invalid' errored if: matrix.instance == 'invalid' && steps.test-vault-action.outcome != 'failure' diff --git a/.github/workflows/test-login-to-gar.yaml b/.github/workflows/test-login-to-gar.yaml index d2bb92691..c084ecdca 100644 --- a/.github/workflows/test-login-to-gar.yaml +++ b/.github/workflows/test-login-to-gar.yaml @@ -20,15 +20,19 @@ on: merge_group: -permissions: - contents: read - id-token: write +permissions: {} jobs: test: + permissions: + contents: read + id-token: write + runs-on: ubuntu-latest + # Don't run for forks - they don't have access to secrets if: github.event.pull_request.head.repo.full_name == github.repository + steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 diff --git a/actions/build-push-to-dockerhub/README.md b/actions/build-push-to-dockerhub/README.md index 720b7a4da..79167ac0e 100644 --- a/actions/build-push-to-dockerhub/README.md +++ b/actions/build-push-to-dockerhub/README.md @@ -3,10 +3,13 @@ > [!NOTE] > If you are at Grafana Labs: > -> - A docker mirror is available on our self-hosted runners, see [the internal documentation](https://enghub.grafana-ops.net/docs/default/component/deployment-tools/platform/continuous-integration/#docker-caching-in-github-actions) for more info. +> - A docker mirror is available on our self-hosted runners, see [the internal +> documentation](https://enghub.grafana-ops.net/docs/default/component/deployment-tools/platform/continuous-integration/#docker-caching-in-github-actions) +> for more info. -This is a composite GitHub Action, used to build Docker images and push them to DockerHub. -It uses `get-vault-secrets` action to get the DockerHub username and password from Vault. +This is a composite GitHub Action, used to build Docker images and push them to +DockerHub. It uses `get-vault-secrets` action to get the DockerHub username and +password from Vault. Example of how to use this action in a repository: @@ -64,4 +67,8 @@ jobs: - If you specify `platforms` then the action will use buildx to build the image. - You must create a Dockerhub repo before you are able to push to it. -- Most projects should be using Google Artifact Registry (instead of Dockerhub) to store their images. You can see more about that in the push-to-gar-docker shared workflow. +- Most projects at Grafana Labs should be using Google Artifact Registry instead + of Dockerhub to store their images. You can see more about that in the + [push-to-gar-docker] shared workflow. + +[push-to-gar-docker]: ../push-to-gar-docker/README.md diff --git a/actions/build-push-to-dockerhub/action.yaml b/actions/build-push-to-dockerhub/action.yaml index c7c0cfe3a..4872b833f 100644 --- a/actions/build-push-to-dockerhub/action.yaml +++ b/actions/build-push-to-dockerhub/action.yaml @@ -103,7 +103,16 @@ runs: images: ${{ inputs.repository }} tags: ${{ inputs.tags }} - - name: Build and push Docker image + # The `context` input is flagged by Zizmor as a [sink]. This means that with + # the upstream action the user's input to the input ends up in an output, + # and so if it's not handled properly, it could lead to a template injection + # attack. In this action, we pass through the inputs, but we don't then pass + # back the outputs, so we should be fine. Even if we did pass back the + # outputs, we consider ourselves a proxy, so in that case our job would be + # to warn users but not to take any action. + # + # [sink]: https://github.blog/security/application-security/how-to-secure-your-github-actions-workflows-with-codeql/#models + - name: Build and push Docker image # zizmor: ignore[template-injection] uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: ${{ inputs.context }} diff --git a/actions/login-to-gcs/action.yaml b/actions/login-to-gcs/action.yaml index 6d29bbab4..e754c4629 100644 --- a/actions/login-to-gcs/action.yaml +++ b/actions/login-to-gcs/action.yaml @@ -78,9 +78,11 @@ runs: if: ${{ inputs.delete_credentials_file == 'true' && env.GOOGLE_APPLICATION_CREDENTIALS != '' }} shell: sh run: | - if [ -f "${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" ]; then - rm -f "${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" + if [ -f "${GOOGLE_APPLICATION_CREDENTIALS}" ]; then + rm -f "${GOOGLE_APPLICATION_CREDENTIALS}" echo "::notice::Successfully deleted credentials file" else - echo "::warning::Credentials file not found at ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" + echo "::warning::Credentials file not found at ${GOOGLE_APPLICATION_CREDENTIALS}" fi + env: + GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }} diff --git a/actions/push-to-gar-docker/README.md b/actions/push-to-gar-docker/README.md index d4a5820a2..fd826664c 100644 --- a/actions/push-to-gar-docker/README.md +++ b/actions/push-to-gar-docker/README.md @@ -3,11 +3,16 @@ > [!NOTE] > If you are at Grafana Labs: > -> - Follow these steps in the [internal documentation](https://enghub.grafana-ops.net/docs/default/component/deployment-tools/platform/continuous-integration/google-artifact-registry/) to set up a repository before using this action. -> - A docker mirror is available on our self-hosted runners, see [the internal documentation](https://enghub.grafana-ops.net/docs/default/component/deployment-tools/platform/continuous-integration/#docker-caching-in-github-actions) for more info. +> - Follow these steps in the [internal +> documentation](https://enghub.grafana-ops.net/docs/default/component/deployment-tools/platform/continuous-integration/google-artifact-registry/) +> to set up a repository before using this action. +> - A docker mirror is available on our self-hosted runners, see [the internal +> documentation](https://enghub.grafana-ops.net/docs/default/component/deployment-tools/platform/continuous-integration/#docker-caching-in-github-actions) +> for more info. -This is a composite GitHub Action, used to push docker images to Google Artifact Registry (GAR). -It uses [OIDC authentication](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) +This is a composite GitHub Action, used to push docker images to Google Artifact +Registry (GAR). It uses [OIDC +authentication](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect), which means that only workflows which get triggered based on certain rules can trigger these composite workflows. @@ -80,6 +85,10 @@ input. ## Outputs +> [!IMPORTANT] +> Be careful when handling the `metadata` output. This contains user-supplied +> information, and so it can be a vector for template injection. + The following outputs are exposed from [`docker/metadata-action`](https://github.com/docker/metadata-action?tab=readme-ov-file#outputs) and [`docker/build-push-action`](https://github.com/docker/build-push-action?tab=readme-ov-file#outputs): | Name | Type | Description | From | diff --git a/actions/push-to-gar-docker/action.yaml b/actions/push-to-gar-docker/action.yaml index 9e3daa564..2c3e4deb4 100644 --- a/actions/push-to-gar-docker/action.yaml +++ b/actions/push-to-gar-docker/action.yaml @@ -173,7 +173,15 @@ runs: version: latest # see https://github.com/docker/build-push-action/issues/1345#issuecomment-2770572479 buildkitd-config: ${{ runner.environment == 'self-hosted' && '/etc/buildkitd.toml' || '' }} - - name: Build the container + # The `context` input is flagged by Zizmor as a [sink]. This means that with + # the upstream action the user's input to the input ends up in an output, + # and so if it's not handled properly, it could lead to a template injection + # attack. In this action, we do pass this back out via our `metadata` + # output. However, we consider ourselves a proxy, so in that case our job is + # to warn users but not to take any action. + # + # [sink]: https://github.blog/security/application-security/how-to-secure-your-github-actions-workflows-with-codeql/#models + - name: Build the container # zizmor: ignore[template-injection] uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 id: build with: @@ -207,9 +215,11 @@ runs: if: ${{ inputs.delete_credentials_file == 'true' && env.GOOGLE_APPLICATION_CREDENTIALS != '' }} shell: sh run: | - if [ -f "${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" ]; then - rm -f "${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" + if [ -f "${GOOGLE_APPLICATION_CREDENTIALS}" ]; then + rm -f "${GOOGLE_APPLICATION_CREDENTIALS}" echo "::notice::Successfully deleted credentials file" else - echo "::warning::Credentials file not found at ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" + echo "::warning::Credentials file not found at ${GOOGLE_APPLICATION_CREDENTIALS}" fi + env: + GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }} diff --git a/actions/push-to-gcs/action.yaml b/actions/push-to-gcs/action.yaml index eb480b193..d08940723 100644 --- a/actions/push-to-gcs/action.yaml +++ b/actions/push-to-gcs/action.yaml @@ -120,9 +120,11 @@ runs: if: ${{ inputs.delete_credentials_file == 'true' && env.GOOGLE_APPLICATION_CREDENTIALS != '' }} shell: sh run: | - if [ -f "${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" ]; then - rm -f "${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" + if [ -f "${GOOGLE_APPLICATION_CREDENTIALS}" ]; then + rm -f "${GOOGLE_APPLICATION_CREDENTIALS}" echo "::notice::Successfully deleted credentials file" else - echo "::warning::Credentials file not found at ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}" + echo "::warning::Credentials file not found at ${GOOGLE_APPLICATION_CREDENTIALS}" fi + env: + GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}