From 57fe475b7c1cd96199d747817e21c4fbb0f8b71b Mon Sep 17 00:00:00 2001 From: Hayden B Date: Thu, 14 Mar 2024 23:46:37 -0700 Subject: [PATCH] Trigger preproduction sync only after merge to main (#1170) In #916, we restructured the sync scripts. As part of this, the sync to preprod after a ceremony completed would occur from the ceremony branch. We only allow workload impersonation (which is needed to push to GCS and update the CDN cache) from main, so this breaks. To fix this, we simply split the workflow into two: The first triggers on a push to ceremony and creates a PR to merge to main. After merging from main and updating the root, we sync all contents from the repository directory. I also removed the cron job because I don't think it's needed. Also updated documentation for post-merge steps. Signed-off-by: Hayden Blauzvern --- .github/workflows/sync-ceremony-to-main.yml | 73 +------------ .github/workflows/sync-main-to-preprod.yml | 109 ++++++++++++++++++++ playbooks/ORCHESTRATION.md | 16 +-- 3 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 .github/workflows/sync-main-to-preprod.yml diff --git a/.github/workflows/sync-ceremony-to-main.yml b/.github/workflows/sync-ceremony-to-main.yml index b7dd0fd1..58ffed4a 100644 --- a/.github/workflows/sync-ceremony-to-main.yml +++ b/.github/workflows/sync-ceremony-to-main.yml @@ -13,9 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This workflow is triggered when a ceremony branch is completed, and will: -# - Create a PR that merges the completed ceremony branch to main -# - Sync the ceremony branch to the GCS preprod bucket +# This workflow is triggered when a ceremony branch is completed, +# creating a PR that merges the completed ceremony branch to main name: Sync Published Ceremony Branch to Main and Preprod @@ -23,8 +22,6 @@ name: Sync Published Ceremony Branch to Main and Preprod permissions: {} on: - schedule: - - cron: '0 */12 * * *' # every 12 hours workflow_dispatch: inputs: branch: @@ -63,73 +60,9 @@ jobs: pr_body: "Merge ceremony branch to main" pr_reviewer: bobcallaway,haydentherapper,joshuagl,kommendorkapten - sync: - permissions: - id-token: 'write' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - with: - fetch-depth: 0 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 - with: - go-version-file: './go.mod' - check-latest: true - # Setup OIDC->SA auth - - uses: google-github-actions/auth@55bd3a7c6e2ae7cf1877fd1ccb9d54c0503c457c # v2.1.2 - id: auth - with: - token_format: 'access_token' - workload_identity_provider: 'projects/306323169285/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider' - service_account: 'tuf-gha@project-rekor.iam.gserviceaccount.com' - create_credentials_file: true - - uses: google-github-actions/setup-gcloud@98ddc00a17442e89a24bbf282954a3b65ce6d200 # v2.1.0 - with: - project_id: project-rekor - - name: Login - run: | - gcloud auth login --brief --cred-file="${{ steps.auth.outputs.credentials_file_path }}" - gcloud auth list - # sync - - name: sync - run: | - check_expiration() { - expiry=$(jq -r '.signed.expires' $1) - expires=$(date -d $expiry +%s) - current=$(date +%s) - if (( expires < current )); then - echo "Detected expired metadata file $1 at $expiry!" - exit 1 - fi; - } - # Upload all but TUF timestamp. Once timestamp is uploaded, all other files must have been uploaded. - for f in $(ls repository/repository/ -I *timestamp.json) - do - # Check for expiration if this is a non-versioned metadata file. - # Versioned metadata like 1.root.json may be expired. - # TODO(asraa): When consistent snapshots are enabled, this logic must be changed so that - # only old versioned metadata can be expired. - if [[ $f == [^0-9]*.json ]]; then - check_expiration repository/repository/$f - fi; - gcloud --quiet storage cp --cache-control=no-store -r repository/repository/$f gs://sigstore-preprod-tuf-root/ - done - # Upload timestamp after checking latest timestamp expiration - check_expiration repository/repository/timestamp.json - gcloud --quiet storage cp --cache-control=no-store -r repository/repository/*timestamp.json gs://sigstore-preprod-tuf-root/ - # delete any files present in sigstore-preprod-tuf-root not in repository/repository - gcloud --quiet storage cp -r gs://sigstore-preprod-tuf-root/ . - diff -qr repository/repository sigstore-preprod-tuf-root | while read l; do - if [[ $l =~ "Only in sigstore-preprod-tuf-root" ]]; then - path=$(python3 -c "import re; s='$l'; pattern=r'^Only in sigstore-preprod-tuf-root(\/?)(.*): (.*)$'; match=re.search(pattern, s); print('/'.join([match.group(2), match.group(3)]).lstrip('/'))") - gcloud --quiet storage rm gs://sigstore-preprod-tuf-root/$path - fi; - done - gcloud compute url-maps invalidate-cdn-cache tuf-preprod-repo-cdn-lb --path "/*" --async - if-failed: runs-on: ubuntu-latest - needs: [sync] + needs: [push] permissions: issues: 'write' actions: 'read' diff --git a/.github/workflows/sync-main-to-preprod.yml b/.github/workflows/sync-main-to-preprod.yml new file mode 100644 index 00000000..a0d9eaac --- /dev/null +++ b/.github/workflows/sync-main-to-preprod.yml @@ -0,0 +1,109 @@ +# +# Copyright 2024 The Sigstore Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This workflow is triggered a root signing completes and is merged +# into main, syncing the repository to the GCS preprod bucket + +name: Sync Published Root Signing to Preprod + +# Declare default permissions as none. +permissions: {} + +on: + workflow_dispatch: + push: + # When a root signing ceremony completes + branches: + - main + paths: + - 'repository/repository/root.json' + +jobs: + sync: + permissions: + id-token: 'write' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 + - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version-file: './go.mod' + check-latest: true + # Setup OIDC->SA auth + - uses: google-github-actions/auth@55bd3a7c6e2ae7cf1877fd1ccb9d54c0503c457c # v2.1.2 + id: auth + with: + token_format: 'access_token' + workload_identity_provider: 'projects/306323169285/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider' + service_account: 'tuf-gha@project-rekor.iam.gserviceaccount.com' + create_credentials_file: true + - uses: google-github-actions/setup-gcloud@98ddc00a17442e89a24bbf282954a3b65ce6d200 # v2.1.0 + with: + project_id: project-rekor + - name: Login + run: | + gcloud auth login --brief --cred-file="${{ steps.auth.outputs.credentials_file_path }}" + gcloud auth list + # sync + - name: sync + run: | + check_expiration() { + expiry=$(jq -r '.signed.expires' $1) + expires=$(date -d $expiry +%s) + current=$(date +%s) + if (( expires < current )); then + echo "Detected expired metadata file $1 at $expiry!" + exit 1 + fi; + } + # Upload all but TUF timestamp. Once timestamp is uploaded, all other files must have been uploaded. + for f in $(ls repository/repository/ -I *timestamp.json) + do + # Check for expiration if this is a non-versioned metadata file. + # Versioned metadata like 1.root.json may be expired. + # TODO(asraa): When consistent snapshots are enabled, this logic must be changed so that + # only old versioned metadata can be expired. + if [[ $f == [^0-9]*.json ]]; then + check_expiration repository/repository/$f + fi; + gcloud --quiet storage cp --cache-control=no-store -r repository/repository/$f gs://sigstore-preprod-tuf-root/ + done + # Upload timestamp after checking latest timestamp expiration + check_expiration repository/repository/timestamp.json + gcloud --quiet storage cp --cache-control=no-store -r repository/repository/*timestamp.json gs://sigstore-preprod-tuf-root/ + # delete any files present in sigstore-preprod-tuf-root not in repository/repository + gcloud --quiet storage cp -r gs://sigstore-preprod-tuf-root/ . + diff -qr repository/repository sigstore-preprod-tuf-root | while read l; do + if [[ $l =~ "Only in sigstore-preprod-tuf-root" ]]; then + path=$(python3 -c "import re; s='$l'; pattern=r'^Only in sigstore-preprod-tuf-root(\/?)(.*): (.*)$'; match=re.search(pattern, s); print('/'.join([match.group(2), match.group(3)]).lstrip('/'))") + gcloud --quiet storage rm gs://sigstore-preprod-tuf-root/$path + fi; + done + gcloud compute url-maps invalidate-cdn-cache tuf-preprod-repo-cdn-lb --path "/*" --async + + if-failed: + runs-on: ubuntu-latest + needs: [sync] + permissions: + issues: 'write' + actions: 'read' + if: always() && needs.sync.result == 'failure' + steps: + - name: Open issue or add comment on failure + uses: sigstore/sigstore-probers/.github/actions/open-workflow-issue@main + with: + comment_for_each_failure: true diff --git a/playbooks/ORCHESTRATION.md b/playbooks/ORCHESTRATION.md index c325a87e..9d3e1b68 100644 --- a/playbooks/ORCHESTRATION.md +++ b/playbooks/ORCHESTRATION.md @@ -389,21 +389,23 @@ required. ## Step 7: Publication -Once the PR from [Step 3](#step-3-snapshotting-and-timestamping) is merged, a [workflow](../.github/workflows/sync-ceremony-to-main.yml) will automatically create a PR merging the changes on the completed ceremony branch to main. +Once the PR from [Step 6](#step-6-snapshotting-and-timestamping) is merged, a [workflow](../.github/workflows/sync-ceremony-to-main.yml) will automatically create a PR merging the changes on the completed ceremony branch to main. -Submitting this PR will trigger a push to the preproduction GCS bucket, so ensure that this PR is verified and ready to be pushed! +Submitting this PR will trigger [a workflow](../.github/workflows/sync-main-to-preprod.yml) to push to the preproduction GCS bucket for further testing, so ensure that this PR is verified and ready to be pushed! ## Post-ceremony Steps -1. The preproduction GCS bucket will need to be manually synced to the GCS production bucket as of [916](https://github.com/sigstore/root-signing/pull/916). +1. Perform manual testing against the preproduction environment. Assuming no issues, continue with the following steps. -2. If any root keyholders have changed, update the [current root keyholders](https://github.com/sigstore/root-signing#current-sigstore-root-keyholders) with their name, key ID, and location of their key material. +1. To push to the production environment, run [this workflow](../.github/workflows/sync-preprod-to-prod.yml), which syncs the preproduction and production GCS buckets. -3. If any targets have changed, update them and their usage in the table containing the [repository structure](https://github.com/sigstore/root-signing#tuf-repository-structure). +1. If any root keyholders have changed, update the [current root keyholders](https://github.com/sigstore/root-signing#current-sigstore-root-keyholders) with their name, key ID, and location of their key material. -4. Announce the root rotation on twitter and the community meeting, and thank the keyholders! +1. If any targets have changed, update them and their usage in the table containing the [repository structure](https://github.com/sigstore/root-signing#tuf-repository-structure). -5. Schedule the next root signing event one month before expiration on the calendar. Check [here](https://github.com/sigstore/root-signing/blob/e3f1fe5e487984f525afc81ac77fa5ce39737d0f/cmd/tuf/app/init.go#L29) for root expiration. Schedule a testing event for the week before. +1. Announce the root rotation on twitter and the community meeting, and thank the keyholders! + +1. Schedule the next root signing event one month before expiration on the calendar. Check [here](https://github.com/sigstore/root-signing/blob/e3f1fe5e487984f525afc81ac77fa5ce39737d0f/cmd/tuf/app/init.go#L29) for root expiration. Schedule a testing event for the week before. ### Other