diff --git a/cli/environment-variables.mdx b/cli/environment-variables.mdx index 6a3eff4e..907e7a35 100644 --- a/cli/environment-variables.mdx +++ b/cli/environment-variables.mdx @@ -171,7 +171,7 @@ For storing and securing environment variables, we advise the following: 1. Store local environment variables in your shell or in `.env` files that are not committed to your git repo. Add those files to your `.gitignore` file. -2. In a CI context, e.g. GitHub Actions, store sensitive variables as secrets. Almost all CI providers have such a feature. +2. In a CI context, load sensitive values from your CI secret store or an external secrets manager. For large secret sets, see [Manage secrets at scale](/platform/manage-secrets-at-scale). 3. For remote variables, store sensitive data as secrets in Checkly. For more information on how to manage secrets in Checkly see [variables and secrets](/platform/variables). All variables are stored encrypted at rest and in transfer. diff --git a/constructs/browser-check.mdx b/constructs/browser-check.mdx index 2ce18331..2474cedf 100644 --- a/constructs/browser-check.mdx +++ b/constructs/browser-check.mdx @@ -40,6 +40,7 @@ new BrowserCheck("browser-check-1", { ```ts Advanced Example import { BrowserCheck, Frequency } from "checkly/constructs" +import { secret } from "checkly/util" import * as path from "path" new BrowserCheck("advanced-browser-check", { @@ -52,7 +53,7 @@ new BrowserCheck("advanced-browser-check", { runtimeId: "2025.04", environmentVariables: [ { key: "TEST_USERNAME", value: "testuser" }, - { key: "TEST_PASSWORD", value: "{{SECRET_PASSWORD}}", secret: true }, + { key: "TEST_PASSWORD", value: secret("TEST_PASSWORD"), secret: true }, ], code: { entrypoint: path.join(__dirname, "advanced-flow.spec.ts"), @@ -167,6 +168,8 @@ new BrowserCheck("quick-test", { ``` ```ts Complex Test File +import { secret } from "checkly/util" + new BrowserCheck("e2e-purchase", { name: "E-commerce Purchase Flow", code: { @@ -174,7 +177,7 @@ new BrowserCheck("e2e-purchase", { }, environmentVariables: [ { key: "TEST_USER_EMAIL", value: "test@example.com" }, - { key: "TEST_PASSWORD", value: "{{SECRET_PASSWORD}}", secret: true }, + { key: "TEST_PASSWORD", value: secret("TEST_PASSWORD"), secret: true }, ], }) ``` @@ -379,12 +382,14 @@ Check-level environment variables that will be available during test execution. **Usage:** -```ts highlight={3-6} +```ts highlight={5-8} +import { secret } from "checkly/util" + new BrowserCheck("my-check", { name: "My Browser Check", environmentVariables: [ { key: "TEST_USERNAME", value: "testuser@example.com" }, - { key: "TEST_PASSWORD", value: "{{SECRET_PASSWORD}}", secret: true }, + { key: "TEST_PASSWORD", value: secret("TEST_PASSWORD"), secret: true }, ], /* More options... */ }) @@ -403,11 +408,13 @@ new BrowserCheck("my-check", { ```ts Test Credentials +import { secret } from "checkly/util" + new BrowserCheck("user-flow", { name: "User Account Flow", environmentVariables: [ { key: "TEST_EMAIL", value: "test@example.com" }, - { key: "TEST_PASSWORD", value: "{{TEST_USER_PASSWORD}}", secret: true }, + { key: "TEST_PASSWORD", value: secret("TEST_PASSWORD"), secret: true }, { key: "BASE_URL", value: "https://staging.example.com" }, ], code: { entrypoint: path.join(__dirname, "user-flow.spec.ts") }, diff --git a/constructs/check-group-v2.mdx b/constructs/check-group-v2.mdx index cf422f68..a7f94a14 100644 --- a/constructs/check-group-v2.mdx +++ b/constructs/check-group-v2.mdx @@ -39,6 +39,7 @@ new ApiCheck("api-check-1", { ```ts Advanced Example import { ApiCheck, CheckGroupV2, EmailAlertChannel, Frequency } from "checkly/constructs" +import { secret } from "checkly/util" const emailChannel = new EmailAlertChannel("team-email", { address: "team@example.com", @@ -54,7 +55,7 @@ const group = new CheckGroupV2("comprehensive-group", { concurrency: 5, environmentVariables: [ { key: "API_BASE_URL", value: "https://api.example.com" }, - { key: "API_KEY", value: "{{SECRET_API_KEY}}", secret: true }, + { key: "API_KEY", value: secret("API_KEY"), secret: true }, ], alertChannels: [emailChannel], browserChecks: { @@ -389,4 +390,3 @@ See [Mixing Checks and Monitors in a Group](/platform/groups/#mixing-checks-and- ``` - diff --git a/constructs/check-group.mdx b/constructs/check-group.mdx index 51a0db6b..c9e6fa4c 100644 --- a/constructs/check-group.mdx +++ b/constructs/check-group.mdx @@ -44,6 +44,7 @@ import { EmailAlertChannel, Frequency, } from "checkly/constructs" +import { secret } from "checkly/util" const emailChannel = new EmailAlertChannel("team-email", { address: "team@example.com", @@ -59,7 +60,7 @@ const group = new CheckGroup("comprehensive-group", { concurrency: 5, environmentVariables: [ { key: "API_BASE_URL", value: "https://api.example.com" }, - { key: "API_KEY", value: "{{SECRET_API_KEY}}", secret: true }, + { key: "API_KEY", value: secret("API_KEY"), secret: true }, ], alertChannels: [emailChannel], browserChecks: { diff --git a/constructs/multistep-check.mdx b/constructs/multistep-check.mdx index 0172928c..628567f1 100644 --- a/constructs/multistep-check.mdx +++ b/constructs/multistep-check.mdx @@ -39,6 +39,7 @@ new MultiStepCheck("multistep-check-1", { ```ts Advanced Example import { MultiStepCheck, Frequency } from "checkly/constructs" +import { secret } from "checkly/util" import * as path from "path" new MultiStepCheck("complex-multistep-check", { @@ -52,7 +53,7 @@ new MultiStepCheck("complex-multistep-check", { environmentVariables: [ { key: "BASE_URL", value: "https://app.example.com" }, { key: "TEST_USER_EMAIL", value: "test@example.com" }, - { key: "TEST_USER_PASSWORD", value: "{{SECRET_PASSWORD}}", secret: true }, + { key: "TEST_USER_PASSWORD", value: secret("TEST_USER_PASSWORD"), secret: true }, ], code: { entrypoint: path.join(__dirname, "user-journey.spec.ts"), diff --git a/constructs/playwright-check.mdx b/constructs/playwright-check.mdx index b80c2f6a..e9154e40 100644 --- a/constructs/playwright-check.mdx +++ b/constructs/playwright-check.mdx @@ -460,13 +460,15 @@ Check-level environment variables that will be available during test execution. **Usage:** -```ts highlight={4-7} +```ts highlight={6-9} +import { secret } from "checkly/util" + new PlaywrightCheck("user-flow-pwt-check-suite", { name: "User Flow Playwright Check Suite", playwrightConfigPath: "../playwright.config.ts", environmentVariables: [ { key: "TEST_USERNAME", value: "testuser@example.com" }, - { key: "TEST_PASSWORD", value: "{{SECRET_PASSWORD}}", secret: true }, + { key: "TEST_PASSWORD", value: secret("TEST_PASSWORD"), secret: true }, ], }) ``` @@ -484,12 +486,14 @@ new PlaywrightCheck("user-flow-pwt-check-suite", { ```ts Test Credentials +import { secret } from "checkly/util" + new PlaywrightCheck("user-flow-pwt-check-suite", { name: "User Flow Playwright Check Suite", playwrightConfigPath: "../playwright.config.ts", environmentVariables: [ { key: "TEST_USERNAME", value: "testuser@example.com" }, - { key: "TEST_PASSWORD", value: "{{SECRET_PASSWORD}}", secret: true }, + { key: "TEST_PASSWORD", value: secret("TEST_PASSWORD"), secret: true }, ], }) ``` diff --git a/docs.json b/docs.json index 7a65a8db..e79ab470 100644 --- a/docs.json +++ b/docs.json @@ -99,6 +99,7 @@ ] }, "platform/secrets", + "platform/manage-secrets-at-scale", "platform/dynamic-secret-scrubbing", "platform/data-storage", "platform/variables", @@ -434,7 +435,16 @@ "group": "GitHub", "pages": [ "integrations/ci-cd/github/actions", - "integrations/ci-cd/github/deployments" + "integrations/ci-cd/github/deployments", + { + "group": "External secrets", + "pages": [ + "integrations/ci-cd/github/secrets/aws-secrets-manager", + "integrations/ci-cd/github/secrets/google-secret-manager", + "integrations/ci-cd/github/secrets/azure-key-vault", + "integrations/ci-cd/github/secrets/hashicorp-vault" + ] + } ] }, "integrations/ci-cd/gitlab/overview", diff --git a/integrations/ci-cd/github/actions.mdx b/integrations/ci-cd/github/actions.mdx index 2688677f..a8d8cf93 100644 --- a/integrations/ci-cd/github/actions.mdx +++ b/integrations/ci-cd/github/actions.mdx @@ -250,4 +250,5 @@ If the workflow uses path filters, make sure it still creates the required Check - Learn more about [`checkly test`](/cli/checkly-test). - Learn more about [`checkly trigger`](/cli/checkly-trigger). +- [Deploy scoped Checkly secrets from an external secrets manager](/platform/manage-secrets-at-scale). - Use [GitHub deployment hooks](/integrations/ci-cd/github/deployments) when you want Checkly to react directly to GitHub deployment events. diff --git a/integrations/ci-cd/github/secrets/aws-secrets-manager.mdx b/integrations/ci-cd/github/secrets/aws-secrets-manager.mdx new file mode 100644 index 00000000..736b6ddc --- /dev/null +++ b/integrations/ci-cd/github/secrets/aws-secrets-manager.mdx @@ -0,0 +1,140 @@ +--- +title: "Deploy Checkly secrets from AWS Secrets Manager" +sidebarTitle: "AWS Secrets Manager" +description: "Use GitHub Actions and OIDC to deploy scoped Checkly secrets from AWS Secrets Manager." +canonical: "https://www.checklyhq.com/docs/integrations/ci-cd/github/secrets/aws-secrets-manager/" +--- + +Use GitHub OpenID Connect (OIDC) to obtain short-lived AWS credentials, read the secrets required by one Checkly CLI project, and deploy them as group- or check-level secrets. For the architecture and scoping model, see [Manage secrets at scale](/platform/manage-secrets-at-scale). + + + - A Checkly CLI project with `checkly` installed as a dependency. + - An AWS account with your values stored in AWS Secrets Manager. + - A GitHub repository with Actions enabled. + - A protected GitHub environment named `checkly-production`. Restrict its deployment branches and reviewers as appropriate. + - The repository or environment variables `AWS_ROLE_ARN`, `AWS_REGION`, and `CHECKLY_ACCOUNT_ID`. + - Permission to create an AWS IAM OIDC provider and role. + + + + This guide uses a fictional payments service; replace its names, paths, and + URL with your own application values. Checkly requires `CHECKLY_API_KEY` and + `CHECKLY_ACCOUNT_ID`; `AWS_ROLE_ARN` and `AWS_REGION` configure this workflow. + + +## Configure AWS access + +Follow AWS's guide for [using Secrets Manager in GitHub jobs](https://docs.aws.amazon.com/secretsmanager/latest/userguide/retrieving-secrets_github.html) to create an OIDC provider and IAM role. Restrict the role's trust policy to the repository and protected GitHub environment that deploy Checkly. + +Grant the role `secretsmanager:GetSecretValue` only for the secrets listed in this workflow. The retrieval action also requires `secretsmanager:ListSecrets`; secrets encrypted with a customer-managed KMS key require `kms:Decrypt` on that key. Store the role ARN and region in `AWS_ROLE_ARN` and `AWS_REGION` GitHub Actions variables. + +## Scope secrets in your constructs + +Use `secret()` to read `CHECKLY_SECRET_*` from the deploy process. Set `secret: true` so Checkly stores the value as a non-retrievable secret. Attach each value at the narrowest level that needs it: + +```typescript checks/payments.check.ts +import { ApiCheck, CheckGroupV2, Frequency } from "checkly/constructs"; +import { secret } from "checkly/util"; + +const paymentsGroup = new CheckGroupV2("payments", { + name: "Payments", + activated: true, + muted: false, + frequency: Frequency.EVERY_10M, + locations: ["us-east-1"], + environmentVariables: [ + { + key: "PAYMENTS_API_KEY", + value: secret("PAYMENTS_API_KEY"), + secret: true, + }, + ], +}); + +new ApiCheck("payments-admin-api", { + name: "Payments admin API", + group: paymentsGroup, + environmentVariables: [ + { + key: "PAYMENTS_ADMIN_TOKEN", + value: secret("PAYMENTS_ADMIN_TOKEN"), + secret: true, + }, + ], + request: { + method: "GET", + url: "https://payments.example.com/admin/health", + headers: [ + { key: "Authorization", value: "Bearer {{PAYMENTS_API_KEY}}" }, + { key: "X-Admin-Token", value: "{{PAYMENTS_ADMIN_TOKEN}}" }, + ], + }, +}); +``` + +`secret('PAYMENTS_API_KEY')` reads `CHECKLY_SECRET_PAYMENTS_API_KEY` during deployment and fails if it is missing. The shorter key `PAYMENTS_API_KEY` is what your check receives at runtime. + +## Fetch and deploy in GitHub Actions + +Fetch each AWS secret with an explicit `CHECKLY_SECRET_*` alias. The retrieval action masks the values and adds them to the environment of subsequent steps. + +```yaml .github/workflows/checkly-deploy.yml +name: Deploy Checkly + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + environment: checkly-production + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Authenticate to AWS + uses: aws-actions/configure-aws-credentials@v6 + with: + role-to-assume: ${{ vars.AWS_ROLE_ARN }} + aws-region: ${{ vars.AWS_REGION }} + allowed-account-ids: "123456789012" + mask-aws-account-id: true + role-session-name: checkly-${{ github.run_id }} + + - name: Fetch deployment secrets + uses: aws-actions/aws-secretsmanager-get-secrets@v3 + with: + secret-ids: | + CHECKLY_API_KEY, checkly/production/api-key + CHECKLY_SECRET_PAYMENTS_API_KEY, checkly/production/payments-api-key + CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN, checkly/production/payments-admin-token + + - name: Deploy Checkly + env: + CHECKLY_ACCOUNT_ID: ${{ vars.CHECKLY_ACCOUNT_ID }} + run: npx checkly deploy --force +``` + +Install dependencies before fetching secrets, and keep deployment as the final step. Values created by `aws-secretsmanager-get-secrets` are available to every later step in the job, not only the deploy command. + + + Do not print or persist fetched values. Keep deployment as the final step that + receives them. + + +## Rotate a secret + +When you rotate a value in AWS Secrets Manager, run this workflow again. Checkly receives a copy during `checkly deploy`; rotation in AWS does not update an already deployed Checkly secret automatically. diff --git a/integrations/ci-cd/github/secrets/azure-key-vault.mdx b/integrations/ci-cd/github/secrets/azure-key-vault.mdx new file mode 100644 index 00000000..ad59610b --- /dev/null +++ b/integrations/ci-cd/github/secrets/azure-key-vault.mdx @@ -0,0 +1,165 @@ +--- +title: 'Deploy Checkly secrets from Azure Key Vault' +sidebarTitle: 'Azure Key Vault' +description: 'Authenticate to Azure with GitHub OIDC, load deployment secrets from Azure Key Vault, and deploy them at group or check scope.' +canonical: 'https://www.checklyhq.com/docs/integrations/ci-cd/github/secrets/azure-key-vault/' +--- + +Use GitHub OpenID Connect (OIDC) to give a trusted deployment job short-lived access to Azure Key Vault. The job maps each Key Vault secret to the `CHECKLY_SECRET_*` name expected by your Checkly constructs, then runs `checkly deploy`. + +For the scoping model and operational guidance behind this workflow, read [Manage secrets at scale](/platform/manage-secrets-at-scale). + + + - A Checkly Monitoring as Code project that declares secrets with `secret()` and `secret: true` at [group or check scope](/platform/manage-secrets-at-scale#choose-the-narrowest-scope). + - An Azure Key Vault that uses the Azure role-based access control (RBAC) permission model. + - A Microsoft Entra application or user-assigned managed identity with a [federated identity credential for GitHub Actions](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust). + - The identity has the `Key Vault Secrets User` role on the vault that contains this project's secrets. + - `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_SUBSCRIPTION_ID`, `AZURE_KEY_VAULT`, and `CHECKLY_ACCOUNT_ID` configured as GitHub Actions variables. + - The Checkly API key stored in Key Vault as `checkly-api-key`, alongside the secrets required by this project. + + + + This guide uses a fictional payments service; replace its names and URL with + your own application values. Checkly requires `CHECKLY_API_KEY` and + `CHECKLY_ACCOUNT_ID`; the `AZURE_*` variables configure this workflow. + + +## Configure Azure access + +Follow Microsoft's [GitHub OIDC setup guide](https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure-openid-connect) to create a federated identity for the repository and GitHub environment that deploy Checkly. + +Assign the identity the read-only [`Key Vault Secrets User` role](https://learn.microsoft.com/en-us/azure/key-vault/general/rbac-guide#azure-built-in-roles-for-key-vault-data-plane-operations) at the narrowest practical scope. The GitHub Actions runner must be able to reach the vault. + +## Map Key Vault secrets to Checkly + +The following example assumes your application uses two credentials stored in Key Vault as `payments-api-key` and `payments-admin-token`. These are example names, not Azure or Checkly requirements. Reference their deployment environment names in your constructs: + +```typescript __checks__/payments.check.ts +import { ApiCheck, CheckGroupV2 } from 'checkly/constructs' +import { secret } from 'checkly/util' + +const paymentsGroup = new CheckGroupV2('payments', { + name: 'Payments', + environmentVariables: [ + { + key: 'PAYMENTS_API_KEY', + value: secret('PAYMENTS_API_KEY'), + secret: true, + }, + ], +}) + +new ApiCheck('payments-admin-health', { + name: 'Payments admin health', + group: paymentsGroup, + environmentVariables: [ + { + key: 'PAYMENTS_ADMIN_TOKEN', + value: secret('PAYMENTS_ADMIN_TOKEN'), + secret: true, + }, + ], + request: { + method: 'GET', + url: 'https://payments.example.com/admin/health', + headers: [ + { key: 'Authorization', value: 'Bearer {{PAYMENTS_ADMIN_TOKEN}}' }, + ], + }, +}) +``` + +`secret('PAYMENTS_API_KEY')` reads `CHECKLY_SECRET_PAYMENTS_API_KEY` when the CLI loads the project. The Key Vault name and the Checkly environment name do not need to match. + +## Deploy from GitHub Actions + +This workflow installs project dependencies before authenticating to Azure. It retrieves named secrets with [`az keyvault secret show`](https://learn.microsoft.com/en-us/cli/azure/keyvault/secret#az-keyvault-secret-show), masks each retrieved value, exports it only in the deployment shell, and does not write it to `GITHUB_ENV`. + +```yaml .github/workflows/deploy-checkly.yml +name: Deploy Checkly + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + environment: production + permissions: + contents: read + id-token: write + env: + AZURE_CORE_OUTPUT: none + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Sign in to Azure with OIDC + uses: azure/login@v3 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + + - name: Load secrets and deploy + shell: bash + env: + AZURE_KEY_VAULT: ${{ vars.AZURE_KEY_VAULT }} + CHECKLY_ACCOUNT_ID: ${{ vars.CHECKLY_ACCOUNT_ID }} + run: | + set -euo pipefail + + get_secret() { + az keyvault secret show \ + --vault-name "$AZURE_KEY_VAULT" \ + --name "$1" \ + --query value \ + --output tsv + } + + mask_secret() { + local value="${1//%/%25}" + value="${value//$'\r'/%0D}" + value="${value//$'\n'/%0A}" + printf '::add-mask::%s\n' "$value" + } + + CHECKLY_API_KEY="$(get_secret checkly-api-key)" + CHECKLY_SECRET_PAYMENTS_API_KEY="$(get_secret payments-api-key)" + CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN="$(get_secret payments-admin-token)" + + mask_secret "$CHECKLY_API_KEY" + mask_secret "$CHECKLY_SECRET_PAYMENTS_API_KEY" + mask_secret "$CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN" + + export CHECKLY_API_KEY + export CHECKLY_SECRET_PAYMENTS_API_KEY + export CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN + + npx checkly deploy --force +``` + +The job grants only `contents: read` and `id-token: write`. The latter lets `azure/login` request a GitHub OIDC token; it does not grant repository write access. `AZURE_CORE_OUTPUT: none` prevents accidental Azure CLI output, while `--output tsv` explicitly returns only each requested value to the shell. + + + Do not print or persist fetched values. This example assumes token-like, + single-line secret values. + + +## Rotate a secret + +Rotating a value in Azure Key Vault does not automatically update the copy stored by Checkly. Run this trusted deployment workflow again after rotation. The next deployment fetches the current value returned by Key Vault and updates the scoped Checkly secret. diff --git a/integrations/ci-cd/github/secrets/google-secret-manager.mdx b/integrations/ci-cd/github/secrets/google-secret-manager.mdx new file mode 100644 index 00000000..4504e6b8 --- /dev/null +++ b/integrations/ci-cd/github/secrets/google-secret-manager.mdx @@ -0,0 +1,153 @@ +--- +title: 'Deploy Checkly secrets from Google Secret Manager' +sidebarTitle: 'Google Secret Manager' +description: 'Use GitHub OIDC to fetch Google Secret Manager values and deploy them as scoped Checkly secrets.' +canonical: 'https://www.checklyhq.com/docs/integrations/ci-cd/github/secrets/google-secret-manager/' +--- + +Use Workload Identity Federation to authenticate a trusted GitHub Actions deployment to Google Cloud without storing a service account key in GitHub. The workflow fetches only the values its Checkly project needs and exposes them to the Checkly deploy step. + +For guidance on choosing account, group, or check scope, see [Manage secrets at scale](/platform/manage-secrets-at-scale). + + + - A Checkly Monitoring as Code project with its dependencies and lockfile committed. + - `CHECKLY_ACCOUNT_ID` stored as a GitHub Actions variable. + - The Checkly API key and required deployment values stored as enabled secret versions in Google Secret Manager. + - A Google Cloud project with the IAM, Resource Manager, Service Account Credentials, Security Token Service, and Secret Manager APIs enabled. + - Permission to configure Workload Identity Federation, service account impersonation, and IAM access on the required secrets. + + + + This guide uses a fictional payments service; replace its names and URL with + your own application values. Checkly requires `CHECKLY_API_KEY` and + `CHECKLY_ACCOUNT_ID`; the `GCP_*` variables configure this workflow. + + +## Configure Google Cloud access + +Follow Google's [deployment pipeline guide](https://cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines) or the [`google-github-actions/auth` instructions](https://github.com/google-github-actions/auth#workload-identity-federation-through-a-service-account) to configure Workload Identity Federation and a dedicated service account. + +Restrict the Workload Identity Provider to the repository and ref that deploy Checkly. Grant the repository principal `roles/iam.workloadIdentityUser` on the service account, then grant that service account `roles/secretmanager.secretAccessor` only on the secrets listed in the workflow. + +Add these non-secret values as GitHub Actions variables: + +| Variable | Value | +| --- | --- | +| `GCP_PROJECT_ID` | The project ID that contains the secrets. | +| `GCP_WORKLOAD_IDENTITY_PROVIDER` | The full provider resource name, including the project **number** and `/providers/...`. | +| `GCP_SERVICE_ACCOUNT` | The dedicated deployment service account email. | + +## Scope secrets in your constructs + +Read each value with `secret()` and mark it as secret on the group or check that needs it: + +```typescript __checks__/payments.check.ts +import { ApiCheck, CheckGroupV2 } from 'checkly/constructs' +import { secret } from 'checkly/util' + +const paymentsGroup = new CheckGroupV2('payments', { + name: 'Payments', + environmentVariables: [ + { + key: 'PAYMENTS_API_KEY', + value: secret('PAYMENTS_API_KEY'), + secret: true, + }, + ], +}) + +new ApiCheck('payments-admin', { + name: 'Payments admin', + group: paymentsGroup, + environmentVariables: [ + { + key: 'PAYMENTS_ADMIN_TOKEN', + value: secret('PAYMENTS_ADMIN_TOKEN'), + secret: true, + }, + ], + request: { + method: 'GET', + url: 'https://payments.example.com/admin', + headers: [ + { key: 'Authorization', value: 'Bearer {{PAYMENTS_ADMIN_TOKEN}}' }, + ], + }, +}) +``` + +The helper reads `CHECKLY_SECRET_PAYMENTS_API_KEY` and `CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN` when the project is loaded for deployment. If a required value is missing, `secret()` stops the deployment instead of deploying an empty value. Checkly then stores each value at the declared scope. + +## Fetch and deploy from GitHub Actions + +The Google Secret Manager action returns masked step outputs. Map those outputs to `CHECKLY_SECRET_*` variables only on the deploy step: + + + Google maintains `get-secretmanager-secrets` in the `google-github-actions` organization, but its README states that it is not an officially supported Google Cloud product and is not covered by a Google Cloud support contract. + + +```yaml .github/workflows/deploy-checkly.yml +name: Deploy Checkly + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + id-token: write + +jobs: + deploy: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + + - run: npm ci + + - id: google-auth + name: Authenticate to Google Cloud + uses: google-github-actions/auth@v3 + with: + project_id: ${{ vars.GCP_PROJECT_ID }} + workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} + + - id: google-secrets + name: Fetch deployment secrets + uses: google-github-actions/get-secretmanager-secrets@v3 + with: + secrets: |- + checkly_api_key:${{ vars.GCP_PROJECT_ID }}/checkly-api-key + payments_api_key:${{ vars.GCP_PROJECT_ID }}/payments-api-key + payments_admin_token:${{ vars.GCP_PROJECT_ID }}/payments-admin-token + + - name: Deploy Checkly + run: npx checkly deploy --force + env: + CHECKLY_API_KEY: ${{ steps.google-secrets.outputs.checkly_api_key }} + CHECKLY_ACCOUNT_ID: ${{ vars.CHECKLY_ACCOUNT_ID }} + CHECKLY_SECRET_PAYMENTS_API_KEY: ${{ steps.google-secrets.outputs.payments_api_key }} + CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN: ${{ steps.google-secrets.outputs.payments_admin_token }} +``` + +The `id-token: write` permission lets the job request a GitHub OIDC token. Google Cloud IAM still determines which secrets it can read. + + + Do not print or persist fetched values. Expose the outputs only to the Checkly + deployment step. + + +## Rotate a secret + +The short secret names in the workflow fetch the `latest` enabled version. After you add a new version in Google Secret Manager, run this deployment workflow again to update the encrypted value stored by Checkly. Rotation in Google Cloud alone does not update an already deployed Checkly secret. + +When the workflow no longer needs a value, remove it from the construct, workflow mapping, and service account IAM policy. diff --git a/integrations/ci-cd/github/secrets/hashicorp-vault.mdx b/integrations/ci-cd/github/secrets/hashicorp-vault.mdx new file mode 100644 index 00000000..18a06715 --- /dev/null +++ b/integrations/ci-cd/github/secrets/hashicorp-vault.mdx @@ -0,0 +1,140 @@ +--- +title: "Deploy Checkly secrets from HashiCorp Vault" +sidebarTitle: "HashiCorp Vault" +description: "Use GitHub OIDC to fetch HashiCorp Vault values and deploy them as scoped Checkly secrets." +canonical: "https://www.checklyhq.com/docs/integrations/ci-cd/github/secrets/hashicorp-vault/" +--- + +Use GitHub OpenID Connect (OIDC) to authenticate a trusted deployment job to HashiCorp Vault without storing a Vault token in GitHub. The job reads only the values required by one Checkly CLI project and exposes them to the Checkly deploy step. + +For guidance on choosing account, group, or check scope, see [Manage secrets at scale](/platform/manage-secrets-at-scale). + + + - A Checkly Monitoring as Code project with its dependencies and lockfile committed. + - A GitHub repository with Actions enabled and a protected environment named `checkly-production`. + - A Vault server reachable from the GitHub Actions runner, with the required values stored in a KV secrets engine. + - Permission to configure a Vault JWT auth method, policy, and role. + - `VAULT_ADDR` and `CHECKLY_ACCOUNT_ID` configured as GitHub Actions variables. + - The Checkly API key stored in Vault alongside the secrets required by this CLI project. + + + + This guide uses a fictional payments service; replace its names, paths, and + URL with your own application values. Checkly requires `CHECKLY_API_KEY` and + `CHECKLY_ACCOUNT_ID`; `VAULT_ADDR` configures this workflow. + + +## Configure Vault access + +Configure Vault's JWT authentication for GitHub Actions and create a role for this deployment by following HashiCorp's [`vault-action` JWT guide](https://github.com/hashicorp/vault-action#jwt-with-github-oidc-tokens). The workflow below assumes that role is named `checkly-payments-deploy`. + +Restrict the role to the repository, branch, and GitHub environment that deploy Checkly. Its policy should grant read-only access only to the Vault paths referenced by this project. The role's audience must match `jwtGithubAudience` in the workflow. See HashiCorp's [JWT role reference](https://developer.hashicorp.com/vault/api-docs/auth/jwt#createupdate-role) for the Vault configuration options. + +## Scope secrets in your constructs + +Read each value with `secret()` and mark it as secret on the group or check that needs it: + +```typescript __checks__/payments.check.ts +import { ApiCheck, CheckGroupV2 } from "checkly/constructs"; +import { secret } from "checkly/util"; + +const paymentsGroup = new CheckGroupV2("payments", { + name: "Payments", + environmentVariables: [ + { + key: "PAYMENTS_API_KEY", + value: secret("PAYMENTS_API_KEY"), + secret: true, + }, + ], +}); + +new ApiCheck("payments-admin", { + name: "Payments admin", + group: paymentsGroup, + environmentVariables: [ + { + key: "PAYMENTS_ADMIN_TOKEN", + value: secret("PAYMENTS_ADMIN_TOKEN"), + secret: true, + }, + ], + request: { + method: "GET", + url: "https://payments.example.com/admin", + headers: [ + { key: "Authorization", value: "Bearer {{PAYMENTS_ADMIN_TOKEN}}" }, + ], + }, +}); +``` + +The helper reads `CHECKLY_SECRET_PAYMENTS_API_KEY` and `CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN` when the CLI loads the project. If a required value is missing, `secret()` stops the deployment instead of deploying an empty value. + +## Fetch and deploy from GitHub Actions + +The official HashiCorp action authenticates with GitHub's OIDC token and returns masked step outputs. Set `exportEnv: false`, then map the outputs to `CHECKLY_SECRET_*` variables only on the deploy step: + +```yaml .github/workflows/deploy-checkly.yml +name: Deploy Checkly + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + deploy: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + environment: checkly-production + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - id: vault-secrets + name: Fetch deployment secrets + uses: hashicorp/vault-action@v4 + with: + url: ${{ vars.VAULT_ADDR }} + method: jwt + role: checkly-payments-deploy + jwtGithubAudience: https://github.com/YOUR_ORG + exportEnv: false + secrets: | + secret/data/checkly/production/payments/checkly checkly_api_key | CHECKLY_API_KEY ; + secret/data/checkly/production/payments/group payments_api_key | CHECKLY_SECRET_PAYMENTS_API_KEY ; + secret/data/checkly/production/payments/admin-check admin_token | CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN + + - name: Deploy Checkly + env: + CHECKLY_API_KEY: ${{ steps.vault-secrets.outputs.CHECKLY_API_KEY }} + CHECKLY_ACCOUNT_ID: ${{ vars.CHECKLY_ACCOUNT_ID }} + CHECKLY_SECRET_PAYMENTS_API_KEY: ${{ steps.vault-secrets.outputs.CHECKLY_SECRET_PAYMENTS_API_KEY }} + CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN: ${{ steps.vault-secrets.outputs.CHECKLY_SECRET_PAYMENTS_ADMIN_TOKEN }} + run: npx checkly deploy --force +``` + +The job needs `id-token: write` to request the GitHub OIDC token and `contents: read` for checkout. The Vault role and policy determine which values the job can read. + + + Do not print or persist fetched values. Install dependencies before fetching + secrets and expose the outputs only to the Checkly deployment step. + + +## Rotate a secret + +Rotating a value in Vault does not automatically update the encrypted copy stored by Checkly. Run this trusted deployment workflow again after rotation. The action reads the current KV value and `checkly deploy` updates the secret at its declared group or check scope. + +When the workflow no longer needs a value, remove it from the construct, workflow mapping, and Vault policy or path. diff --git a/platform/manage-secrets-at-scale.mdx b/platform/manage-secrets-at-scale.mdx new file mode 100644 index 00000000..ab4be687 --- /dev/null +++ b/platform/manage-secrets-at-scale.mdx @@ -0,0 +1,165 @@ +--- +title: 'Manage secrets at scale' +sidebarTitle: 'Secrets at scale' +description: 'Load secrets from an external secrets manager during deployment and scope them to the Checkly groups and checks that need them.' +canonical: 'https://www.checklyhq.com/docs/platform/manage-secrets-at-scale/' +--- + +Use an external secrets manager as the source of truth when you manage many credentials with Monitoring as Code. Load the values into the deployment environment, then deploy each secret at the narrowest Checkly scope that needs it. + +This prevents every check execution from receiving a large account-wide secret set while keeping secret values out of your repository and CI/CD configuration. + + + - A Checkly Monitoring as Code project using a current version of the Checkly CLI. + - A secrets manager that your CI/CD workload can authenticate to. + - Permission to deploy the checks and groups that will use the secrets. + - Runtime `2024.09` or later. Private Locations require agent `3.3.4` or later. + + +## Understand the names in these examples + +| Name | Meaning | +| --- | --- | +| `CHECKLY_API_KEY` | Required secret credential used by the Checkly CLI. Supply an API key for your Checkly account. | +| `CHECKLY_ACCOUNT_ID` | Required identifier for the Checkly account the CLI deploys to. This value is not secret. | +| `CHECKLY_SECRET_` | Deployment environment variable read by `secret('')`. You define `` for each secret in your project. | +| `PAYMENTS_API_KEY`, `PAYMENTS_ADMIN_TOKEN`, `payments-api-key`, and `payments.example.com` | Fictional application values used only to demonstrate group and check scoping. Replace them with the credentials, names, and URLs used by the service you monitor. | + +## How the deployment works + +```mermaid +flowchart LR + A["CI/CD job"] -->|"short-lived identity"| B["External secrets manager"] + B -->|"required values"| A + A -->|"CHECKLY_SECRET_*"| C["Checkly CLI deploy"] + C -->|"secret: true"| D["Scoped Checkly secrets"] + D --> E["Relevant group and check runs"] +``` + +The secret value exists in the deployment process long enough for the Checkly CLI to synthesize and deploy the construct. Checkly then encrypts it and stores it at the configured group or check scope. + + + `secret()` is a deployment-time environment lookup, not a persistent reference to your external secrets manager. Rotating a value in the external manager does not update Checkly until you deploy again. + + +## Choose the narrowest scope + +Checkly [merges variables when a check runs](/platform/variables#variable-hierarchy). Check-level values override group-level values, which override account-level values. + +| Scope | Use it for | +| --- | --- | +| Account | Values genuinely needed by almost every check in the account. | +| Group | Credentials shared by one service, application, environment, or team. | +| Check | Credentials used by one check only. | + +Each check or group can define up to 100 environment variables. If one deployment project spans many teams or systems, consider splitting it into independently deployed projects so each deployment identity can access only its own secrets. + +## Read secrets during deployment + +The `secret()` helper reads `CHECKLY_SECRET_` from the environment and stops with an error when the value is missing. For example, `secret('PAYMENTS_API_KEY')` reads `CHECKLY_SECRET_PAYMENTS_API_KEY`. + +```typescript secrets.ts +import { secret } from 'checkly/util' + +export const PAYMENTS_API_KEY = secret('PAYMENTS_API_KEY') +export const PAYMENTS_ADMIN_TOKEN = secret('PAYMENTS_ADMIN_TOKEN') +``` + +Your external secrets manager can use different names. Map its identifiers to the `CHECKLY_SECRET_*` names expected by the project when you fetch the values in CI/CD. + +Every CLI command that loads these constructs needs the corresponding environment values, including `checkly test` and `checkly deploy`. Never provide production secrets to pull request workflows that run untrusted code. + +## Deploy a group-level secret + +```typescript __checks__/payments-group.check.ts +import { ApiCheck, CheckGroupV2, Frequency } from 'checkly/constructs' +import { PAYMENTS_API_KEY } from '../secrets' + +const paymentsGroup = new CheckGroupV2('payments', { + name: 'Payments', + activated: true, + muted: false, + frequency: Frequency.EVERY_10M, + locations: ['us-east-1'], + environmentVariables: [ + { + key: 'PAYMENTS_API_KEY', + value: PAYMENTS_API_KEY, + secret: true, + }, + ], +}) + +new ApiCheck('payments-health', { + name: 'Payments health', + group: paymentsGroup, + request: { + method: 'GET', + url: 'https://payments.example.com/health', + headers: [{ key: 'Authorization', value: 'Bearer {{PAYMENTS_API_KEY}}' }], + }, +}) +``` + +`{{PAYMENTS_API_KEY}}` is resolved by Checkly when the API check runs. It does not read a secret from your CI/CD provider or external secrets manager. + +## Deploy a check-level secret + +Define a secret directly on the check when no other check needs it: + +```typescript __checks__/payments-admin.check.ts +import { ApiCheck } from 'checkly/constructs' +import { PAYMENTS_ADMIN_TOKEN } from '../secrets' + +new ApiCheck('payments-admin-health', { + name: 'Payments admin health', + activated: true, + muted: false, + frequency: 10, + locations: ['us-east-1'], + environmentVariables: [ + { + key: 'PAYMENTS_ADMIN_TOKEN', + value: PAYMENTS_ADMIN_TOKEN, + secret: true, + }, + ], + request: { + method: 'GET', + url: 'https://payments.example.com/admin/health', + headers: [{ key: 'Authorization', value: 'Bearer {{PAYMENTS_ADMIN_TOKEN}}' }], + }, +}) +``` + +Inside Browser, Multi-Step, Playwright, and API setup or teardown scripts, read the deployed value with `process.env.PAYMENTS_ADMIN_TOKEN` instead. + +## Treat the deployment as authoritative + +For checks and groups managed by a Checkly CLI project, the construct is the desired state: + +- Changing a declared secret manually in the Checkly UI is reverted by the next deployment. +- Adding an undeclared group- or check-level secret in the UI removes it on the next deployment. +- Removing a declared secret in the UI recreates it on the next deployment. + +Manage both the secret declaration and its scope in code. Manage the value in the external secrets manager. + +## Secure the deployment job + +- Prefer workload identity federation or OIDC over long-lived cloud credentials. +- Grant the deployment identity read access only to the secrets required by that project. +- Fetch secrets only in trusted deployment workflows. Do not expose production secrets to workflows triggered from forks or untrusted pull requests. +- Avoid printing command output that contains secret values. Mask dynamically retrieved values according to your CI/CD provider's guidance. +- Expose secrets only to the step that runs `npx checkly deploy` where your workflow system allows it. +- Split large projects along team or service ownership boundaries to reduce the number of secrets available to one deployment process. + +## Deploy from GitHub Actions + +Use the guide for your secrets manager: + +- [AWS Secrets Manager](/integrations/ci-cd/github/secrets/aws-secrets-manager) +- [Google Secret Manager](/integrations/ci-cd/github/secrets/google-secret-manager) +- [Azure Key Vault](/integrations/ci-cd/github/secrets/azure-key-vault) +- [HashiCorp Vault](/integrations/ci-cd/github/secrets/hashicorp-vault) + +For Browser and Multi-Step checks that must retrieve credentials while the check is running, see [Dynamic Secret Scrubbing](/platform/dynamic-secret-scrubbing). Runtime retrieval is a separate pattern with different check-type support. diff --git a/platform/secrets.mdx b/platform/secrets.mdx index eee36e21..743428cf 100644 --- a/platform/secrets.mdx +++ b/platform/secrets.mdx @@ -11,6 +11,10 @@ Secrets handle the truly sensitive elements—API keys, passwords, authenticatio Both variables and secrets are encrypted at rest and in flight. However, Secrets go further by ensuring that once stored, their values become completely invisible to users, appearing only as masked values in interfaces while remaining fully functional in your monitoring code. + + If you manage many credentials with Monitoring as Code, learn how to [load them from an external secrets manager and deploy them at group or check scope](/platform/manage-secrets-at-scale). + + ## Secrets in Practice @@ -149,4 +153,4 @@ Secrets also work in alert-channel configurations — webhook URLs, API keys, Pa Use dedicated test users, test cards etc. These test users should have minimal privileges in your app. Do not use your admin or root user. Make sure you can easily disable or block these users without recourse. -Keep secrets separate from your browser scripts and store them as environment secrets in Checkly. This way you can reuse secrets in multiple scripts and rotate them as needed. \ No newline at end of file +Keep secrets separate from your browser scripts and store them as environment secrets in Checkly. This way you can reuse secrets in multiple scripts and rotate them as needed. diff --git a/sitemap.xml b/sitemap.xml index 64b2ecd3..1feacf31 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1224,6 +1224,18 @@ https://www.checklyhq.com/docs/integrations/ci-cd/github/deployments/ + + https://www.checklyhq.com/docs/integrations/ci-cd/github/secrets/aws-secrets-manager/ + + + https://www.checklyhq.com/docs/integrations/ci-cd/github/secrets/azure-key-vault/ + + + https://www.checklyhq.com/docs/integrations/ci-cd/github/secrets/google-secret-manager/ + + + https://www.checklyhq.com/docs/integrations/ci-cd/github/secrets/hashicorp-vault/ + https://www.checklyhq.com/docs/integrations/ci-cd/gitlab/overview/ @@ -1629,6 +1641,9 @@ https://www.checklyhq.com/docs/platform/ip-information/ + + https://www.checklyhq.com/docs/platform/manage-secrets-at-scale/ + https://www.checklyhq.com/docs/platform/managing-false-positives/