|
| 1 | +name: namecheap-secret-sync |
| 2 | + |
| 3 | +on: |
| 4 | + workflow_dispatch: |
| 5 | + |
| 6 | +permissions: |
| 7 | + contents: write |
| 8 | + id-token: write |
| 9 | + pull-requests: write |
| 10 | + |
| 11 | +concurrency: |
| 12 | + group: namecheap-secret-sync |
| 13 | + cancel-in-progress: false |
| 14 | + |
| 15 | +jobs: |
| 16 | + sync: |
| 17 | + # The OIDC trust policy is restricted to the default branch; fail closed if |
| 18 | + # someone dispatches this workflow from another ref. |
| 19 | + if: github.ref == 'refs/heads/main' |
| 20 | + runs-on: ubuntu-24.04 |
| 21 | + timeout-minutes: 10 |
| 22 | + env: |
| 23 | + AWS_REGION: us-west-2 |
| 24 | + SECRET_ID: xnoto-namecheap-api |
| 25 | + SOPS_VERSION: v3.13.3 |
| 26 | + SOPS_SHA256: e5bec3346a873ae91d871550f3e698c1aad962aff462a080e40f25fde17fef6b |
| 27 | + AWS_ROLE_ARN: ${{ vars.NAMECHEAP_SECRET_SYNC_AWS_ROLE_ARN }} |
| 28 | + SECRETS_KMS_KEY_ARN: ${{ vars.NAMECHEAP_SECRET_SYNC_SECRETS_KMS_KEY_ARN }} |
| 29 | + steps: |
| 30 | + - name: Check required configuration |
| 31 | + shell: bash |
| 32 | + run: | |
| 33 | + set -euo pipefail |
| 34 | + set +x |
| 35 | + test -n "$AWS_ROLE_ARN" |
| 36 | + test -n "$SECRETS_KMS_KEY_ARN" |
| 37 | +
|
| 38 | + - name: Check out main |
| 39 | + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 |
| 40 | + with: |
| 41 | + ref: main |
| 42 | + persist-credentials: true |
| 43 | + |
| 44 | + - name: Configure AWS credentials |
| 45 | + uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 |
| 46 | + with: |
| 47 | + aws-region: ${{ env.AWS_REGION }} |
| 48 | + role-to-assume: ${{ env.AWS_ROLE_ARN }} |
| 49 | + role-session-name: namecheap-secret-sync-${{ github.run_id }} |
| 50 | + role-duration-seconds: 900 |
| 51 | + allowed-account-ids: 332355796717 |
| 52 | + mask-aws-account-id: true |
| 53 | + unset-current-credentials: true |
| 54 | + retry-max-attempts: 3 |
| 55 | + action-timeout-s: 30 |
| 56 | + inline-session-policy: >- |
| 57 | + {"Version":"2012-10-17","Statement":[{"Sid":"ReadOnlyNamecheapSecret","Effect":"Allow","Action":"secretsmanager:GetSecretValue","Resource":"arn:aws:secretsmanager:us-west-2:332355796717:secret:xnoto-namecheap-api-*"},{"Sid":"DecryptOnlyRequiredKeys","Effect":"Allow","Action":"kms:Decrypt","Resource":["arn:aws:kms:us-west-2:332355796717:key/0a45c0f6-71dc-4d54-ab33-9df4de1a9e91","${{ env.SECRETS_KMS_KEY_ARN }}"]}]} |
| 58 | +
|
| 59 | + - name: Install verified SOPS |
| 60 | + id: sops |
| 61 | + shell: bash |
| 62 | + run: | |
| 63 | + set -euo pipefail |
| 64 | + set +x |
| 65 | + sops_bin="$RUNNER_TEMP/sops-${SOPS_VERSION}" |
| 66 | + curl --fail --silent --show-error --location --proto '=https' --tlsv1.2 \ |
| 67 | + --output "$sops_bin" \ |
| 68 | + "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.amd64" |
| 69 | + printf '%s %s\n' "$SOPS_SHA256" "$sops_bin" | sha256sum --check --status |
| 70 | + chmod 0755 "$sops_bin" |
| 71 | + printf 'path=%s\n' "$sops_bin" >> "$GITHUB_OUTPUT" |
| 72 | +
|
| 73 | + - name: Update the allowlisted encrypted key |
| 74 | + env: |
| 75 | + SOPS_BIN: ${{ steps.sops.outputs.path }} |
| 76 | + shell: bash |
| 77 | + run: | |
| 78 | + set -euo pipefail |
| 79 | + set +x |
| 80 | + umask 077 |
| 81 | +
|
| 82 | + # Fail rather than create a new key; no decrypted value is emitted. |
| 83 | + "$SOPS_BIN" --decrypt --extract '["namecheap_api_key"]' secrets/secrets.yaml >/dev/null |
| 84 | +
|
| 85 | + namecheap_api_key="$(aws secretsmanager get-secret-value \ |
| 86 | + --secret-id "$SECRET_ID" \ |
| 87 | + --query SecretString \ |
| 88 | + --output text)" |
| 89 | + test -n "$namecheap_api_key" |
| 90 | +
|
| 91 | + # --set edits the existing encrypted document in place, limiting the |
| 92 | + # plaintext lifetime to this shell and avoiding decrypted files. |
| 93 | + json_value="$(printf '%s' "$namecheap_api_key" | jq -Rsc '.')" |
| 94 | + "$SOPS_BIN" --set '["namecheap_api_key"] '"$json_value" secrets/secrets.yaml |
| 95 | + unset json_value namecheap_api_key |
| 96 | +
|
| 97 | + - name: Create a scoped pull request |
| 98 | + env: |
| 99 | + GH_TOKEN: ${{ github.token }} |
| 100 | + shell: bash |
| 101 | + run: | |
| 102 | + set -euo pipefail |
| 103 | + set +x |
| 104 | + branch="automation/secret-sync-namecheap-api-${GITHUB_RUN_ID}" |
| 105 | +
|
| 106 | + git add -- secrets/secrets.yaml |
| 107 | + git diff --cached --check |
| 108 | + if git diff --cached --quiet; then |
| 109 | + printf '%s\n' 'The encrypted value is already current; no pull request was created.' |
| 110 | + exit 0 |
| 111 | + fi |
| 112 | +
|
| 113 | + changed_files="$(git diff --cached --name-only)" |
| 114 | + test "$changed_files" = "secrets/secrets.yaml" |
| 115 | + git switch --create "$branch" |
| 116 | + git config user.name 'github-actions[bot]' |
| 117 | + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' |
| 118 | + git commit -m 'chore: sync Namecheap API key' |
| 119 | + git push --set-upstream origin "$branch" |
| 120 | +
|
| 121 | + pr_url="$(gh pr create \ |
| 122 | + --base main \ |
| 123 | + --head "$branch" \ |
| 124 | + --title 'chore: sync Namecheap API key' \ |
| 125 | + --body 'Automated manual sync of the encrypted `namecheap_api_key` value. Review the encrypted-file diff before merge.')" |
| 126 | + { |
| 127 | + printf 'Created branch: `%s`\n\n' "$branch" |
| 128 | + printf 'Created pull request: %s\n' "$pr_url" |
| 129 | + } >> "$GITHUB_STEP_SUMMARY" |
0 commit comments