From f43ecb29efb016e7f50a5410ff7a323f91d0e990 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Tue, 18 Nov 2025 20:37:21 +0530 Subject: [PATCH 01/24] docs: adds aws lambda secret sync docs --- docs/docs.json | 4 + docs/integrations/platforms/aws/lambda.mdx | 118 +++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 docs/integrations/platforms/aws/lambda.mdx diff --git a/docs/docs.json b/docs/docs.json index c3fa858611..ddbbf3deea 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -490,6 +490,10 @@ "pages": [ "integrations/platforms/ansible", "integrations/platforms/apache-airflow", + { + "group": "AWS", + "pages": ["integrations/platforms/aws/lambda"] + }, { "group": "Kubernetes Operator", "pages": [ diff --git a/docs/integrations/platforms/aws/lambda.mdx b/docs/integrations/platforms/aws/lambda.mdx new file mode 100644 index 0000000000..597c85f9c4 --- /dev/null +++ b/docs/integrations/platforms/aws/lambda.mdx @@ -0,0 +1,118 @@ +--- +title: "AWS Lambda" +sidebarTitle: "AWS Lambda" +description: "Keep AWS Lambda environment variables in sync with Infisical" +--- + +Learn how to sync Infisical secrets to AWS Lambda regardless of how you deploy your function. +This guide covers the following strategies: + +- Infisical SDKs +- AWS Secrets Manager integration +- AWS Systems Manager Parameter Store integration +- AWS CLI + +## Choose your sync strategy + +### 1. Fetch secrets at runtime with Infisical SDKs + +If you control the Lambda code, the simplest method is to fetch secrets directly from Infisical using one of our SDKs. +You can read more about the Infisical SDKs [here](/sdks/overview). + +### 2. Sync secrets using AWS Secrets Manager + +Infisical can continuously push secrets into AWS Secrets Manager. +Configure a secret sync from your Infisical project, and Infisical will keep your Secrets Manager values up to date. Your Lambda function can then reference those secrets directly. +Learn more about the AWS Secrets Manager integration [here](/integrations/secret-syncs/aws-secrets-manager). + +### 3. Sync secrets using AWS Systems Manager Parameter Store + +Similarly, Infisical can automatically sync secrets into AWS Systems Manager Parameter Store. +Once configured, your Parameter Store values will remain up to date and can be referenced by your Lambda function. +Learn more about the Parameter Store integration [here](/integrations/secret-syncs/aws-parameter-store). + +### 4. Push environment variables directly using the AWS CLI + +For straightforward workflows or quick rotations, you can push Infisical secrets directly into Lambda environment variables using the AWS CLI. + +## Prerequisites + +- AWS CLI v2 installed and authenticated +- `jq` installed locally +- An IAM principal with `lambda:UpdateFunctionConfiguration` +- Infisical CLI (`infisical`) configured + +### IAM permissions + +Attach a policy like the one below to the IAM user or role responsible for updating Lambda configuration: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "LambdaConfig", + "Effect": "Allow", + "Action": ["lambda:UpdateFunctionConfiguration"], + "Resource": "*" + } + ] +} +``` + + + {" "} + Replacing Lambda environment variables using the AWS CLI overwrites the entire + `Variables` object. Make sure to export your current values so you can import them + into Infisical.{" "} + + +#### Push secrets to Lambda + +Use the Infisical CLI to export secrets as JSON and pass them to the AWS CLI. +The example below targets a project by ID, but you can also use the `--project` and `--env` flags. +Learn more about `infisical export` [here](/cli/commands/export#infisical-export). + +```bash +FUNCTION_NAME=infisical-env-test +REGION=us-east-1 +PROJECT_ID=1234567890 + +aws lambda update-function-configuration \ + --function-name "$FUNCTION_NAME" \ + --region "$REGION" \ + --environment "$( + infisical export \ + --format=json \ + --projectId="$PROJECT_ID" \ + | jq 'map({(.key): .value}) | add | {Variables: .}' + )" +``` + +On success, the updated `Environment.Variables` block will be returned. +Verify the values in the Lambda console or by invoking the function. + + + {" "} + Automate this step in CI/CD. Run `infisical export` using an Infisical API key + scoped to your project and environment, and trigger the sync as part of your deployment + workflow.{" "} + + +#### Test your Lambda + +Deploy or update your Lambda function, then run a test invocation to confirm the secrets were loaded correctly. +For example, a simple Node.js handler might log the environment variables: + +```javascript +export const handler = async () => { + const allEnvVars = process.env; + console.log("Environment Variables:", JSON.stringify(allEnvVars, null, 2)); +}; +``` + + + We recommend using automatic secret syncs to AWS Secrets Manager or AWS + Systems Manager Parameter Store to keep your secrets continuously in sync and + avoid manually updating the Lambda configuration. + From f0fed07f279f67f71df7d4d55d88c6e1ff8e7493 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Fri, 21 Nov 2025 20:11:46 +0530 Subject: [PATCH 02/24] fix: review comments --- docs/integrations/platforms/aws/lambda.mdx | 45 +++++++--------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/docs/integrations/platforms/aws/lambda.mdx b/docs/integrations/platforms/aws/lambda.mdx index 597c85f9c4..496527651c 100644 --- a/docs/integrations/platforms/aws/lambda.mdx +++ b/docs/integrations/platforms/aws/lambda.mdx @@ -1,7 +1,7 @@ --- title: "AWS Lambda" sidebarTitle: "AWS Lambda" -description: "Keep AWS Lambda environment variables in sync with Infisical" +description: "How to use Infisical secrets in AWS Lambda" --- Learn how to sync Infisical secrets to AWS Lambda regardless of how you deploy your function. @@ -19,19 +19,12 @@ This guide covers the following strategies: If you control the Lambda code, the simplest method is to fetch secrets directly from Infisical using one of our SDKs. You can read more about the Infisical SDKs [here](/sdks/overview). -### 2. Sync secrets using AWS Secrets Manager +### 2. Push via secret sync -Infisical can continuously push secrets into AWS Secrets Manager. -Configure a secret sync from your Infisical project, and Infisical will keep your Secrets Manager values up to date. Your Lambda function can then reference those secrets directly. -Learn more about the AWS Secrets Manager integration [here](/integrations/secret-syncs/aws-secrets-manager). +Configure a secret sync from your Infisical project, and Infisical will keep your Secrets Manager or Parameter Store values up to date. Your Lambda function can then reference those secrets directly. +Learn more about the [AWS Secrets Manager integration](/integrations/secret-syncs/aws-secrets-manager) and the [AWS Parameter Store integration](/integrations/secret-syncs/aws-parameter-store). -### 3. Sync secrets using AWS Systems Manager Parameter Store - -Similarly, Infisical can automatically sync secrets into AWS Systems Manager Parameter Store. -Once configured, your Parameter Store values will remain up to date and can be referenced by your Lambda function. -Learn more about the Parameter Store integration [here](/integrations/secret-syncs/aws-parameter-store). - -### 4. Push environment variables directly using the AWS CLI +### 3. Push environment variables directly using the AWS CLI For straightforward workflows or quick rotations, you can push Infisical secrets directly into Lambda environment variables using the AWS CLI. @@ -93,26 +86,14 @@ On success, the updated `Environment.Variables` block will be returned. Verify the values in the Lambda console or by invoking the function. - {" "} - Automate this step in CI/CD. Run `infisical export` using an Infisical API key - scoped to your project and environment, and trigger the sync as part of your deployment - workflow.{" "} + Automate this step in CI/CD. Run `infisical export` using an Infisical API + Token scoped to your project and environment, and trigger the sync as part of + your deployment workflow. Learn more about the [Infisical API + Token](/cli/commands/login#user:plain-token-output-useful-for-scripting-and-ci-cd). -#### Test your Lambda - -Deploy or update your Lambda function, then run a test invocation to confirm the secrets were loaded correctly. -For example, a simple Node.js handler might log the environment variables: - -```javascript -export const handler = async () => { - const allEnvVars = process.env; - console.log("Environment Variables:", JSON.stringify(allEnvVars, null, 2)); -}; -``` - - + We recommend using automatic secret syncs to AWS Secrets Manager or AWS - Systems Manager Parameter Store to keep your secrets continuously in sync and - avoid manually updating the Lambda configuration. - + Parameter Store to keep your secrets continuously in sync and avoid manually + updating the Lambda configuration. + From 95fdff7747fe3e192b065b9f98b4cc8e65fe89a4 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Mon, 24 Nov 2025 19:59:00 -0300 Subject: [PATCH 03/24] documentation update --- .env.example | 15 +- .../getting-started/introduction.mdx | 8 - docker-swarm/.env-example | 16 -- .../overview/examples/integration.mdx | 90 -------- docs/docs.json | 84 +------- docs/documentation/guides/nextjs-vercel.mdx | 29 +-- docs/documentation/platform/integrations.mdx | 12 -- docs/integrations/app-connections/gitlab.mdx | 2 + .../integrations/app-connections/overview.mdx | 4 - .../{cloud => cicd}/aws-amplify.mdx | 2 +- docs/integrations/cicd/bitbucket.mdx | 24 +-- docs/integrations/{cloud => cicd}/checkly.mdx | 0 docs/integrations/cicd/circleci.mdx | 40 ---- docs/integrations/cicd/codefresh.mdx | 32 --- docs/integrations/cicd/githubactions.mdx | 204 +----------------- docs/integrations/cicd/gitlab.mdx | 74 +------ docs/integrations/cicd/octopus-deploy.mdx | 76 ------- docs/integrations/cicd/rundeck.mdx | 32 --- .../integrations/{cloud => cicd}/teamcity.mdx | 0 docs/integrations/cicd/travisci.mdx | 31 --- .../cloud/aws-parameter-store.mdx | 8 - .../integrations/cloud/aws-secret-manager.mdx | 8 - .../cloud/azure-app-configuration.mdx | 8 - docs/integrations/cloud/azure-devops.mdx | 55 ----- docs/integrations/cloud/azure-key-vault.mdx | 8 - docs/integrations/cloud/cloud-66.mdx | 48 ----- docs/integrations/cloud/cloudflare-pages.mdx | 39 ---- .../integrations/cloud/cloudflare-workers.mdx | 39 ---- docs/integrations/cloud/databricks.mdx | 8 - .../cloud/digital-ocean-app-platform.mdx | 32 --- docs/integrations/cloud/flyio.mdx | 32 --- .../integrations/cloud/gcp-secret-manager.mdx | 8 - docs/integrations/cloud/hashicorp-vault.mdx | 8 - docs/integrations/cloud/hasura-cloud.mdx | 31 --- docs/integrations/cloud/heroku.mdx | 71 ------ docs/integrations/cloud/laravel-forge.mdx | 38 ---- docs/integrations/cloud/netlify.mdx | 66 ------ docs/integrations/cloud/northflank.mdx | 33 --- docs/integrations/cloud/qovery.mdx | 38 ---- docs/integrations/cloud/railway.mdx | 47 ---- docs/integrations/cloud/render.mdx | 9 - docs/integrations/cloud/supabase.mdx | 39 ---- docs/integrations/cloud/terraform-cloud.mdx | 8 - docs/integrations/cloud/vercel.mdx | 8 - docs/integrations/cloud/windmill.mdx | 8 - docs/integrations/overview.mdx | 61 ------ docs/integrations/secret-syncs/overview.mdx | 6 +- docs/self-hosting/configuration/envars.mdx | 104 --------- 48 files changed, 21 insertions(+), 1622 deletions(-) delete mode 100644 docs/api-reference/overview/examples/integration.mdx delete mode 100644 docs/documentation/platform/integrations.mdx rename docs/integrations/{cloud => cicd}/aws-amplify.mdx (96%) rename docs/integrations/{cloud => cicd}/checkly.mdx (100%) delete mode 100644 docs/integrations/cicd/circleci.mdx delete mode 100644 docs/integrations/cicd/codefresh.mdx delete mode 100644 docs/integrations/cicd/octopus-deploy.mdx delete mode 100644 docs/integrations/cicd/rundeck.mdx rename docs/integrations/{cloud => cicd}/teamcity.mdx (100%) delete mode 100644 docs/integrations/cicd/travisci.mdx delete mode 100644 docs/integrations/cloud/aws-parameter-store.mdx delete mode 100644 docs/integrations/cloud/aws-secret-manager.mdx delete mode 100644 docs/integrations/cloud/azure-app-configuration.mdx delete mode 100644 docs/integrations/cloud/azure-devops.mdx delete mode 100644 docs/integrations/cloud/azure-key-vault.mdx delete mode 100644 docs/integrations/cloud/cloud-66.mdx delete mode 100644 docs/integrations/cloud/cloudflare-pages.mdx delete mode 100644 docs/integrations/cloud/cloudflare-workers.mdx delete mode 100644 docs/integrations/cloud/databricks.mdx delete mode 100644 docs/integrations/cloud/digital-ocean-app-platform.mdx delete mode 100644 docs/integrations/cloud/flyio.mdx delete mode 100644 docs/integrations/cloud/gcp-secret-manager.mdx delete mode 100644 docs/integrations/cloud/hashicorp-vault.mdx delete mode 100644 docs/integrations/cloud/hasura-cloud.mdx delete mode 100644 docs/integrations/cloud/heroku.mdx delete mode 100644 docs/integrations/cloud/laravel-forge.mdx delete mode 100644 docs/integrations/cloud/netlify.mdx delete mode 100644 docs/integrations/cloud/northflank.mdx delete mode 100644 docs/integrations/cloud/qovery.mdx delete mode 100644 docs/integrations/cloud/railway.mdx delete mode 100644 docs/integrations/cloud/render.mdx delete mode 100644 docs/integrations/cloud/supabase.mdx delete mode 100644 docs/integrations/cloud/terraform-cloud.mdx delete mode 100644 docs/integrations/cloud/vercel.mdx delete mode 100644 docs/integrations/cloud/windmill.mdx delete mode 100644 docs/integrations/overview.mdx diff --git a/.env.example b/.env.example index f67488c23e..9da28e5282 100644 --- a/.env.example +++ b/.env.example @@ -31,25 +31,14 @@ SMTP_FROM_NAME= SMTP_USERNAME= SMTP_PASSWORD= -# Integration -# Optional only if integration is used -CLIENT_ID_HEROKU= -CLIENT_ID_VERCEL= -CLIENT_ID_NETLIFY= +# CICD Integration CLIENT_ID_GITHUB= CLIENT_ID_GITHUB_APP= CLIENT_SLUG_GITHUB_APP= -CLIENT_ID_GITLAB= -CLIENT_ID_BITBUCKET= -CLIENT_SECRET_HEROKU= -CLIENT_SECRET_VERCEL= -CLIENT_SECRET_NETLIFY= CLIENT_SECRET_GITHUB= CLIENT_SECRET_GITHUB_APP= +CLIENT_ID_GITLAB= CLIENT_SECRET_GITLAB= -CLIENT_SECRET_BITBUCKET= -CLIENT_SLUG_VERCEL= - CLIENT_PRIVATE_KEY_GITHUB_APP= CLIENT_APP_ID_GITHUB_APP= diff --git a/company/documentation/getting-started/introduction.mdx b/company/documentation/getting-started/introduction.mdx index 55b4831949..74694703ea 100644 --- a/company/documentation/getting-started/introduction.mdx +++ b/company/documentation/getting-started/introduction.mdx @@ -95,12 +95,4 @@ Depending on your use case, it might be helpful to look into some of the resourc > Fetch secrets via HTTP request. - - Explore integrations for GitHub, Vercel, AWS, and more. - diff --git a/docker-swarm/.env-example b/docker-swarm/.env-example index a30e3bba6d..8a132914bb 100644 --- a/docker-swarm/.env-example +++ b/docker-swarm/.env-example @@ -25,22 +25,6 @@ SMTP_FROM_NAME= SMTP_USERNAME= SMTP_PASSWORD= -# Integration -# Optional only if integration is used -CLIENT_ID_HEROKU= -CLIENT_ID_VERCEL= -CLIENT_ID_NETLIFY= -CLIENT_ID_GITHUB= -CLIENT_ID_GITLAB= -CLIENT_ID_BITBUCKET= -CLIENT_SECRET_HEROKU= -CLIENT_SECRET_VERCEL= -CLIENT_SECRET_NETLIFY= -CLIENT_SECRET_GITHUB= -CLIENT_SECRET_GITLAB= -CLIENT_SECRET_BITBUCKET= -CLIENT_SLUG_VERCEL= - # Sentry (optional) for monitoring errors SENTRY_DSN= diff --git a/docs/api-reference/overview/examples/integration.mdx b/docs/api-reference/overview/examples/integration.mdx deleted file mode 100644 index 71f5b6de4e..0000000000 --- a/docs/api-reference/overview/examples/integration.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: "Configure native integrations via API" -description: "How to use Infisical API to sync secrets to external secret managers" ---- - -The Infisical API allows you to create programmatic integrations that connect with third-party secret managers to synchronize secrets from Infisical. - -This guide will primarily demonstrate the process using AWS Secret Store Manager (AWS SSM), but the steps are generally applicable to other secret management integrations. - - - For details on setting up AWS SSM synchronization and understanding its prerequisites, refer to the [AWS SSM integration setup documentation](../../../integrations/cloud/aws-secret-manager). - - - - - Authentication is required for all integrations. Use the [Integration Auth API](../../endpoints/integrations/create-auth) with the following parameters to authenticate. - - - Set this parameter to **aws-secret-manager**. - - - The Infisical project ID for the integration. - - - The AWS IAM User Access ID. - - - The AWS IAM User Access Secret Key. - - - ```bash Request - curl --request POST \ - --url https://app.infisical.com/api/v1/integration-auth/access-token \ - --header 'Authorization: ' \ - --header 'Content-Type: application/json' \ - --data '{ - "workspaceId": "", - "integration": "aws-secret-manager", - "accessId": "", - "accessToken": "" - }' - ``` - - - - Once authentication between AWS SSM and Infisical is established, you can configure the synchronization behavior. - This involves specifying the source (environment and secret path in Infisical) and the destination in SSM to which the secrets will be synchronized. - - Use the [integration API](../../endpoints/integrations/create) with the following parameters to configure the sync source and destination. - - - The ID of the integration authentication object used with AWS, obtained from the previous API response. - - - Indicates whether the integration should be active or inactive. - - - The secret name for saving in AWS SSM, which can be arbitrarily chosen. - - - The AWS region where the SSM is located, e.g., `us-east-1`. - - - The Infisical environment slug from which secrets will be synchronized, e.g., `dev`. - - - The Infisical folder path from which secrets will be synchronized, e.g., `/some/path`. The root path is `/`. - - - ```bash Request - curl --request POST \ - --url https://app.infisical.com/api/v1/integration \ - --header 'Authorization: ' \ - --header 'Content-Type: application/json' \ - --data '{ - "integrationAuthId": "", - "sourceEnvironment": "", - "secretPath": "", - "app": "", - "region": "" - }' - ``` - - - - - -Congratulations! You have successfully set up an integration to synchronize secrets from Infisical with AWS SSM. -For more information, [view the integration API reference](../../endpoints/integrations). - \ No newline at end of file diff --git a/docs/docs.json b/docs/docs.json index 57f44596fd..04b9e5efab 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -568,71 +568,15 @@ } ] }, - { - "group": "Native Integrations", - "pages": [ - { - "group": "AWS", - "pages": [ - "integrations/cloud/aws-parameter-store", - "integrations/cloud/aws-secret-manager", - "integrations/cloud/aws-amplify" - ] - }, - "integrations/cloud/vercel", - "integrations/cloud/azure-key-vault", - "integrations/cloud/azure-app-configuration", - "integrations/cloud/azure-devops", - "integrations/cloud/gcp-secret-manager", - { - "group": "Cloudflare", - "pages": [ - "integrations/cloud/cloudflare-pages", - "integrations/cloud/cloudflare-workers" - ] - }, - "integrations/cloud/terraform-cloud", - "integrations/cloud/databricks", - { - "group": "View more", - "pages": [ - "integrations/cloud/digital-ocean-app-platform", - "integrations/cloud/heroku", - "integrations/cloud/netlify", - "integrations/cloud/flyio", - "integrations/cloud/railway", - "integrations/cloud/render", - "integrations/cloud/laravel-forge", - "integrations/cloud/supabase", - "integrations/cloud/northflank", - "integrations/cloud/hasura-cloud", - "integrations/cloud/qovery", - "integrations/cloud/hashicorp-vault", - "integrations/cloud/cloud-66", - "integrations/cloud/windmill" - ] - } - ] - }, { "group": "CI/CD Integrations", "pages": [ - "integrations/cicd/jenkins", + "integrations/cicd/aws-amplify", + "integrations/cicd/bitbucket", + "integrations/cicd/checkly", "integrations/cicd/githubactions", "integrations/cicd/gitlab", - "integrations/cicd/bitbucket", - "integrations/cloud/teamcity", - { - "group": "View more", - "pages": [ - "integrations/cicd/circleci", - "integrations/cicd/travisci", - "integrations/cicd/rundeck", - "integrations/cicd/codefresh", - "integrations/cloud/checkly", - "integrations/cicd/octopus-deploy" - ] - } + "integrations/cicd/jenkins" ] }, { @@ -884,11 +828,7 @@ "group": "Overview", "pages": [ "api-reference/overview/introduction", - "api-reference/overview/authentication", - { - "group": "Examples", - "pages": ["api-reference/overview/examples/integration"] - } + "api-reference/overview/authentication" ] }, { @@ -2490,20 +2430,6 @@ ] } ] - }, - { - "group": "Integrations", - "pages": [ - "api-reference/endpoints/integrations/create-auth", - "api-reference/endpoints/integrations/list-auth", - "api-reference/endpoints/integrations/find-auth", - "api-reference/endpoints/integrations/delete-auth", - "api-reference/endpoints/integrations/delete-auth-by-id", - "api-reference/endpoints/integrations/create", - "api-reference/endpoints/integrations/update", - "api-reference/endpoints/integrations/delete", - "api-reference/endpoints/integrations/list-project-integrations" - ] } ] }, diff --git a/docs/documentation/guides/nextjs-vercel.mdx b/docs/documentation/guides/nextjs-vercel.mdx index ecefb0f2ef..d0cdd639e5 100644 --- a/docs/documentation/guides/nextjs-vercel.mdx +++ b/docs/documentation/guides/nextjs-vercel.mdx @@ -183,32 +183,7 @@ At this stage, you know how to use the Infisical CLI to inject secrets into your ## Infisical-Vercel integration for production environment variables -We'll now use the Infisical-Vercel integration send secrets from Infisical to Vercel as production environment variables. - -### Infisical-Vercel integration - -To begin we have to import the Next.js app into Vercel as a project. [Follow these instructions](https://vercel.com/docs/frameworks/nextjs) to deploy the Next.js app to Vercel. - -Next, navigate to your project's integrations tab in Infisical and press on the Vercel tile to grant Infisical access to your Vercel account. - -![integrations](../../images/integrations.png) - -![integrations vercel authorization](../../images/integrations/vercel/integrations-vercel-auth.png) - - - Opting in for the Infisical-Vercel integration will break end-to-end encryption since Infisical will be able to read - your secrets. This is, however, necessary for Infisical to sync the secrets to Vercel. - - Your secrets remain encrypted at rest following our [security guide mechanics](/internals/security). - - -Now select **Production** for (the source) **Environment** and sync it to the **Production Environment** of the (target) application in Vercel. -Lastly, press create integration to start syncing secrets to Vercel. - -![integrations vercel](../../images/integrations/vercel/integrations-vercel-create.png) -![integrations vercel](../../images/integrations/vercel/integrations-vercel.png) - -You should now see your secret from Infisical appear as production environment variables in your Vercel project. +Use our [Vercel Secret Syncs](../../integrations/secret-syncs/vercel) guide to sync secrets from Infisical to Vercel as production environment variables. At this stage, you know how to use the Infisical-Vercel integration to sync production secrets from Infisical to Vercel. @@ -245,4 +220,4 @@ At this stage, you know how to use the Infisical-Vercel integration to sync prod See also: - [Documentation for the Infisical CLI](/cli/overview) -- [Documentation for the Vercel integration](/integrations/cloud/vercel) +- [Documentation for the Vercel Secret Sync](../../integrations/secret-syncs/vercel) diff --git a/docs/documentation/platform/integrations.mdx b/docs/documentation/platform/integrations.mdx deleted file mode 100644 index 2414c5c147..0000000000 --- a/docs/documentation/platform/integrations.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "Integrations" -description: "How to sync your secrets among various 3rd-party services with Infisical." ---- - -Integrations allow environment variables to be synced across your entire infrastructure from local development to CI/CD and production. - - - View all available integrations and their guides - - -![integrations](../../images/integrations.png) diff --git a/docs/integrations/app-connections/gitlab.mdx b/docs/integrations/app-connections/gitlab.mdx index c9af952a73..d17322540e 100644 --- a/docs/integrations/app-connections/gitlab.mdx +++ b/docs/integrations/app-connections/gitlab.mdx @@ -12,6 +12,8 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access Using the GitLab Connection with OAuth on a self-hosted instance of Infisical requires configuring an OAuth application in GitLab and registering your instance with it. + If you're self-hosting Gitlab with custom certificates, you will have to configure your Infisical instance to trust these certificates. To learn how, please follow [this guide](../../self-hosting/guides/custom-certificates). + **Prerequisites:** - A GitLab account with existing projects - Self-hosted Infisical instance diff --git a/docs/integrations/app-connections/overview.mdx b/docs/integrations/app-connections/overview.mdx index 8b1032e7df..1201ca3caa 100644 --- a/docs/integrations/app-connections/overview.mdx +++ b/docs/integrations/app-connections/overview.mdx @@ -75,10 +75,6 @@ to limit the access of this entity to the minimal permission set required to per 4. Utilize the Connection: Use your App Connection for various features across Infisical such as our Secrets Sync by selecting it via the dropdown menu in the UI or by passing the associated `connectionId` when generating resources via the API. - - Infisical is continuously expanding its third-party application support. If your desired application isn't listed, - you can still use previous methods of connecting to it such as our Native Integrations. - ## Platform Managed Credentials diff --git a/docs/integrations/cloud/aws-amplify.mdx b/docs/integrations/cicd/aws-amplify.mdx similarity index 96% rename from docs/integrations/cloud/aws-amplify.mdx rename to docs/integrations/cicd/aws-amplify.mdx index 6d3123b10f..9b4b81ccb5 100644 --- a/docs/integrations/cloud/aws-amplify.mdx +++ b/docs/integrations/cicd/aws-amplify.mdx @@ -108,7 +108,7 @@ This approach enables you to fetch secrets from Infisical during Amplify build t - Follow the [Infisical AWS SSM Parameter Store Integration Guide](./aws-parameter-store) to set up the integration. Pause once you reach the step where it asks you to select the path you would like to sync. + Follow the [Infisical AWS SSM Parameter Store Secret Syncs Guide](../secret-syncs/aws-parameter-store) to set up the integration. Pause once you reach the step where it asks you to select the path you would like to sync. ![amplify app id](../../images/integrations/aws/integrations-amplify-app-id.png) diff --git a/docs/integrations/cicd/bitbucket.mdx b/docs/integrations/cicd/bitbucket.mdx index 3c1330308d..44893a5b60 100644 --- a/docs/integrations/cicd/bitbucket.mdx +++ b/docs/integrations/cicd/bitbucket.mdx @@ -12,29 +12,7 @@ Prerequisites: - - - Navigate to your project's integrations tab in Infisical. - - ![integrations](/images/integrations.png) - - Press on the Bitbucket tile and grant Infisical access to your Bitbucket account. - - ![integrations bitbucket authorization](/images/integrations/bitbucket/integrations-bitbucket.png) - - - Select which workspace, repository, and optionally, deployment environment, you'd like to sync your secrets - to. - ![integrations configure - bitbucket](/images/integrations/bitbucket/integrations-bitbucket-configuration.png) - - Once created, your integration will begin syncing secrets to the configured repository or deployment - environment. - - ![integrations bitbucket](/images/integrations/bitbucket/integrations-bitbucket.png) - - - + Use our [Bitbucket Secret Syncs](../secret-syncs/bitbucket) diff --git a/docs/integrations/cloud/checkly.mdx b/docs/integrations/cicd/checkly.mdx similarity index 100% rename from docs/integrations/cloud/checkly.mdx rename to docs/integrations/cicd/checkly.mdx diff --git a/docs/integrations/cicd/circleci.mdx b/docs/integrations/cicd/circleci.mdx deleted file mode 100644 index 5bf04822dc..0000000000 --- a/docs/integrations/cicd/circleci.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: "CircleCI" -description: "How to sync secrets from Infisical to CircleCI" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain an API token in User Settings > Personal API Tokens - - ![integrations circleci token](/images/integrations/circleci/integrations-circleci-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](/images/integrations.png) - - Press on the CircleCI tile and input your CircleCI API token to grant Infisical access to your CircleCI account. - - ![integrations circleci authorization](/images/integrations/circleci/integrations-circleci-auth.png) - - - - Select which Infisical environment secrets you want to sync to which CircleCI project or context. - - - ![integrations circle ci project](/images/integrations/circleci/integrations-circleci-create-project.png) - - - ![integrations circle ci project](/images/integrations/circleci/integrations-circleci-create-context.png) - - - - Finally, press create integration to start syncing secrets to CircleCI. - ![integrations circleci](/images/integrations/circleci/integrations-circleci.png) - - - diff --git a/docs/integrations/cicd/codefresh.mdx b/docs/integrations/cicd/codefresh.mdx deleted file mode 100644 index cf69ae04d3..0000000000 --- a/docs/integrations/cicd/codefresh.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: "Codefresh" -description: "How to sync secrets from Infisical to Codefresh" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain an API key in User Settings > API Keys - - ![integrations codefresh dashboard](../../images/integrations/codefresh/integrations-codefresh-dashboard.png) - ![integrations codefresh token](../../images/integrations/codefresh/integrations-codefresh-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Codefresh tile and input your Codefresh API key to grant Infisical access to your Codefresh account. - - ![integrations codefresh authorization](../../images/integrations/codefresh/integrations-codefresh-auth.png) - - - - Select which Infisical environment secrets you want to sync to which Codefresh service and press create integration to start syncing secrets to Codefresh. - - ![create integration codefresh](../../images/integrations/codefresh/integrations-codefresh-create.png) - ![integrations codefresh](../../images/integrations/codefresh/integrations-codefresh.png) - - \ No newline at end of file diff --git a/docs/integrations/cicd/githubactions.mdx b/docs/integrations/cicd/githubactions.mdx index 82076874c8..fa4387413f 100644 --- a/docs/integrations/cicd/githubactions.mdx +++ b/docs/integrations/cicd/githubactions.mdx @@ -4,204 +4,6 @@ description: "How to sync secrets from Infisical to GitHub Actions" --- - Alternatively, you can use Infisical's official GitHub Action - [here](https://github.com/Infisical/secrets-action). - - -Infisical lets you sync secrets to GitHub at the organization-level, repository-level, and repository environment-level. - -## Connecting with GitHub App (Recommended) - - - - - - Navigate to your project's integrations tab in Infisical and press on the GitHub tile. - - ![integrations](../../images/integrations/github/app/integration-overview.png) - - Select GitHub App as the authentication method and click **Connect to GitHub**. - - ![integrations github app auth selection](../../images/integrations/github/app/github-app-method-selection.png) - - You will then be redirected to the GitHub app installation page. - - ![integrations github app installation](../../images/integrations/github/app/github-app-installation.png) - - Install and authorize the GitHub application. This will redirect you back to the Infisical integration page. - - - - Select which Infisical environment secrets you want to sync to which GitHub organization, repository, or repository environment. - - - - ![integrations github](../../images/integrations/github/integrations-github-scope-repo.png) - - - ![integrations github](../../images/integrations/github/integrations-github-scope-org.png) - - When using the organization scope, your secrets will be saved in the top-level of your GitHub Organization. - - You can choose the visibility, which defines which repositories can access the secrets. The options are: - - **All public repositories**: All public repositories in the organization can access the secrets. - - **All private repositories**: All private repositories in the organization can access the secrets. - - **Selected repositories**: Only the selected repositories can access the secrets. This gives a more fine-grained control over which repositories can access the secrets. You can select _both_ private and public repositories with this option. - - - ![integrations github](../../images/integrations/github/integrations-github-scope-env.png) - - - - Finally, press create integration to start syncing secrets to GitHub. - - ![integrations github](../../images/integrations/github/integrations-github.png) - - - - - - Using the GitHub integration with app authentication on a self-hosted instance of Infisical requires configuring an application on GitHub - and registering your instance with it. - - - Navigate to the GitHub app settings [here](https://github.com/settings/apps). Click **New GitHub App**. - - ![integrations github app create](../../images/integrations/github/app/self-hosted-github-app-create.png) - - Give the application a name, a homepage URL (your self-hosted domain i.e. `https://your-domain.com`), and a callback URL (i.e. `https://your-domain.com/integrations/github/oauth2/callback`). - - ![integrations github app basic details](../../images/integrations/github/app/self-hosted-github-app-basic-details.png) - - Enable request user authorization during app installation. - ![integrations github app enable auth](../../images/integrations/github/app/self-hosted-github-app-enable-oauth.png) - - Disable webhook by unchecking the Active checkbox. - ![integrations github app webhook](../../images/integrations/github/app/self-hosted-github-app-webhook.png) - - Set the repository permissions as follows: Metadata: Read-only, Secrets: Read and write, Environments: Read and write, Actions: Read. - ![integrations github app repository](../../images/integrations/github/app/self-hosted-github-app-repository.png) - - Similarly, set the organization permissions as follows: Secrets: Read and write. - ![integrations github app organization](../../images/integrations/github/app/self-hosted-github-app-organization.png) - - Create the Github application. - ![integrations github app create confirm](../../images/integrations/github/app/self-hosted-github-app-create-confirm.png) - - - If you have a GitHub organization, you can create an application under it - in your organization Settings > Developer settings > GitHub Apps > New GitHub App. - - - - Generate a new **Client Secret** for your GitHub application. - ![integrations github app create secret](../../images/integrations/github/app/self-hosted-github-app-secret.png) - - Generate a new **Private Key** for your Github application. - ![integrations github app create private key](../../images/integrations/github/app/self-hosted-github-app-private-key.png) - - Obtain the necessary Github application credentials. This would be the application slug, client ID, app ID, client secret, and private key. - ![integrations github app credentials](../../images/integrations/github/app/self-hosted-github-app-credentials.png) - - Back in your Infisical instance, add the five new environment variables for the credentials of your GitHub application: - - - `CLIENT_ID_GITHUB_APP`: The **Client ID** of your GitHub application. - - `CLIENT_SECRET_GITHUB_APP`: The **Client Secret** of your GitHub application. - - `CLIENT_SLUG_GITHUB_APP`: The **Slug** of your GitHub application. This is the one found in the URL. - - `CLIENT_APP_ID_GITHUB_APP`: The **App ID** of your GitHub application. - - `CLIENT_PRIVATE_KEY_GITHUB_APP`: The **Private Key** of your GitHub application. - - Once added, restart your Infisical instance and use the GitHub integration via app authentication. - - - - - - -## Connecting with GitHub OAuth - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) -- Ensure that you have admin privileges to the repository you want to sync secrets to. - - - - - - Navigate to your project's integrations tab in Infisical and press on the GitHub tile. - ![integrations](../../images/integrations/github/integration-overview.png) - - Select OAuth as the authentication method and click **Connect to GitHub**. - ![integrations github oauth auth selection](../../images/integrations/github/github-oauth-method-selection.png) - - Grant Infisical access to your GitHub account (organization and repo privileges). - ![integrations github authorization](../../images/integrations/github/integrations-github-auth.png) - - - - Select which Infisical environment secrets you want to sync to which GitHub organization, repository, or repository environment. - - - - ![integrations github](../../images/integrations/github/integrations-github-scope-repo.png) - - - ![integrations github](../../images/integrations/github/integrations-github-scope-org.png) - - When using the organization scope, your secrets will be saved in the top-level of your GitHub Organization. - - You can choose the visibility, which defines which repositories can access the secrets. The options are: - - **All public repositories**: All public repositories in the organization can access the secrets. - - **All private repositories**: All private repositories in the organization can access the secrets. - - **Selected repositories**: Only the selected repositories can access the secrets. This gives a more fine-grained control over which repositories can access the secrets. You can select _both_ private and public repositories with this option. - - - ![integrations github](../../images/integrations/github/integrations-github-scope-env.png) - - - - Finally, press create integration to start syncing secrets to GitHub. - - ![integrations github](../../images/integrations/github/integrations-github.png) - - - - - - Using the GitHub integration on a self-hosted instance of Infisical requires configuring an OAuth application in GitHub - and registering your instance with it. - - - Navigate to your user Settings > Developer settings > OAuth Apps to create a new GitHub OAuth application. - - ![integrations github config](../../images/integrations/github/integrations-github-config-settings.png) - ![integrations github config](../../images/integrations/github/integrations-github-config-dev-settings.png) - ![integrations github config](../../images/integrations/github/integrations-github-config-new-app.png) - - Create the OAuth application. As part of the form, set the **Homepage URL** to your self-hosted domain `https://your-domain.com` - and the **Authorization callback URL** to `https://your-domain.com/integrations/github/oauth2/callback`. - - ![integrations github config](../../images/integrations/github/integrations-github-config-new-app-form.png) - - - If you have a GitHub organization, you can create an OAuth application under it - in your organization Settings > Developer settings > OAuth Apps > New Org OAuth App. - - - - Obtain the **Client ID** and generate a new **Client Secret** for your GitHub OAuth application. - - ![integrations github config](../../images/integrations/github/integrations-github-config-credentials.png) - - Back in your Infisical instance, add two new environment variables for the credentials of your GitHub OAuth application: - - - `CLIENT_ID_GITHUB`: The **Client ID** of your GitHub OAuth application. - - `CLIENT_SECRET_GITHUB`: The **Client Secret** of your GitHub OAuth application. - - Once added, restart your Infisical instance and use the GitHub integration. - - - - - + Use our [GitHub Secret Syncs](../secret-syncs/github) to sync secrets to GitHub at the organization-level, repository-level, and repository environment-level. + Alternatively, you can use Infisical's official GitHub Action [here](https://github.com/Infisical/secrets-action). + \ No newline at end of file diff --git a/docs/integrations/cicd/gitlab.mdx b/docs/integrations/cicd/gitlab.mdx index 2da61ef770..7cbf9512e5 100644 --- a/docs/integrations/cicd/gitlab.mdx +++ b/docs/integrations/cicd/gitlab.mdx @@ -3,41 +3,13 @@ title: "GitLab" description: "How to sync secrets from Infisical to GitLab" --- - - + Prerequisites: - - Set up and add envars to [Infisical Cloud](https://app.infisical.com) + - Set up and add envars to [Infisical Cloud](https://app.infisical.com). - - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the GitLab tile and grant Infisical access to your GitLab account. - - ![integrations gitlab authorization](../../images/integrations/gitlab/integrations-gitlab-auth.png) - - - - Select which Infisical environment secrets you want to sync to which GitLab repository and press create integration to start syncing secrets to GitLab. - - ![integrations gitlab](../../images/integrations/gitlab/integrations-gitlab-create.png) - - Note that the GitLab integration supports a few options in the **Options** tab: - - - Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced. - - Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced. - - Setting a secret prefix or suffix ensures that existing secrets in GitLab are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GitLab without the specified prefix or suffix. - - ![integrations gitlab options](../../images/integrations/gitlab/integrations-gitlab-create-options.png) - - ![integrations gitlab](../../images/integrations/gitlab/integrations-gitlab.png) - - + Use our [GitLab Secret Syncs](../secret-syncs/gitlab) @@ -70,42 +42,4 @@ description: "How to sync secrets from Infisical to GitLab" - - - - - Using the GitLab integration on a self-hosted instance of Infisical requires configuring an application in GitLab - and registering your instance with it. - If you're self-hosting Gitlab with custom certificates, you will have to configure your Infisical instance to trust these certificates. To learn how, please follow [this guide](../../self-hosting/guides/custom-certificates). - - - Navigate to your user Settings > Applications to create a new GitLab application. - - ![integrations gitlab config](../../images/integrations/gitlab/integrations-gitlab-config-edit-profile.png) - ![integrations gitlab config](../../images/integrations/gitlab/integrations-gitlab-config-new-app.png) - - Create the application. As part of the form, set the **Redirect URI** to `https://your-domain.com/integrations/gitlab/oauth2/callback`. - - ![integrations gitlab config](../../images/integrations/gitlab/integrations-gitlab-config-new-app-form.png) - - - If you have a GitLab group, you can create an OAuth application under it - in your group Settings > Applications. - - - - Obtain the **Application ID** and **Secret** for your GitLab application. - - ![integrations gitlab config](../../images/integrations/gitlab/integrations-gitlab-config-credentials.png) - - Back in your Infisical instance, add two new environment variables for the credentials of your GitLab application: - - - `CLIENT_ID_GITLAB`: The **Client ID** of your GitLab application. - - `CLIENT_SECRET_GITLAB`: The **Secret** of your GitLab application. - - Once added, restart your Infisical instance and use the GitLab integration. - - - - - + \ No newline at end of file diff --git a/docs/integrations/cicd/octopus-deploy.mdx b/docs/integrations/cicd/octopus-deploy.mdx deleted file mode 100644 index 90f06e09a8..0000000000 --- a/docs/integrations/cicd/octopus-deploy.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: "Octopus Deploy" -description: "Learn how to sync secrets from Infisical to Octopus Deploy" ---- - -Prerequisites: - -- Set up and add secrets to [Infisical Cloud](https://app.infisical.com) - - - - Navigate to **Configuration** > **Users** and click on the **Create Service Account** button. - - ![integrations octopus deploy - users](/images/integrations/octopus-deploy/integrations-octopus-deploy-user-settings.png) - - Fill out the required fields and click on the **Save** button. - ![integrations octopus deploy service - account](/images/integrations/octopus-deploy/integrations-octopus-deploy-create-service-account.png) - - - On the **Service Account** user page, expand the **API Keys** section and click on the **New API Key** button. - - ![integrations octopus deploy - new api key](/images/integrations/octopus-deploy/integrations-octopus-deploy-create-api-key.png) - - Fill out the required fields and click on the **Generate New** button. - - ![integrations octopus deploy - generate api key](/images/integrations/octopus-deploy/integrations-octopus-deploy-generate-api-key.png) - - If you configure your access token to expire, - you will need to generate a new API key for Infisical prior to this date to keep your integration running. - - Copy the generated **API Key** and click on the **Close** button. - - ![integrations octopus deploy - copy api key](/images/integrations/octopus-deploy/integrations-octopus-deploy-copy-api-key.png) - - - You can skip creating a new team if you already have an Octopus Deploy team configured with - the **Project Contributor** role to assign your Service Account to. - - Navigate to **Configuration** > **Teams** and click on the **Add Team** button. - - ![integrations octopus deploy - teams](/images/integrations/octopus-deploy/integrations-octopus-deploy-team-settings.png) - - Create a new team for **Service Accounts** and click on the **Save** button. - ![integrations octopus deploy add - team](/images/integrations/octopus-deploy/integrations-octopus-deploy-create-team.png) - - On the **Members** tab, click on the **Add Member** button, add your **Infisical Service Account** and click on the **Add** button. - ![integrations octopus deploy add service account to team](/images/integrations/octopus-deploy/integrations-octopus-deploy-add-to-team.png) - - On the **User Roles** tab, click on the **Include User Role** button, and add the **Project Contributor** role. Optionally, - click on the **Define Scope** button to further refine what projects your Service Account has access to. Click on the **Apply** button once complete. - ![integrations octopus deploy add user roles to team](/images/integrations/octopus-deploy/integrations-octopus-deploy-add-role.png) - - Save your team changes by clicking on the **Save** button. - ![integrations octopus deploy save team changes](/images/integrations/octopus-deploy/integrations-octopus-deploy-save-team.png) - - - In Infisical, navigate to your **Project** > **Integrations** page and select the **Octopus Deploy** integration. - ![integration octopus deploy](/images/integrations/octopus-deploy/integrations-octopus-deploy-integrations.png) - - Enter your **Instance URL** and **API Key** from **Octopus Deploy** to authorize Infisical. - ![integration octopus deploy](/images/integrations/octopus-deploy/integrations-octopus-deploy-authorize.png) - - Select a **Space** and **Project** from **Octopus Deploy** to sync secrets to; configuring additional **Scope Values** as needed. Click on the **Create Integration** button once configured. - ![integration octopus deploy](/images/integrations/octopus-deploy/integrations-octopus-deploy-create.png) - - Your Infisical secrets will begin to sync to **Octopus Deploy**. - ![integration octopus deploy](/images/integrations/octopus-deploy/integrations-octopus-deploy-sync.png) - - \ No newline at end of file diff --git a/docs/integrations/cicd/rundeck.mdx b/docs/integrations/cicd/rundeck.mdx deleted file mode 100644 index bda7d81627..0000000000 --- a/docs/integrations/cicd/rundeck.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: "Rundeck" -description: "How to sync secrets from Infisical to Rundeck" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a User API Token in the Profile settings of Rundeck - - ![integrations rundeck token](../../images/integrations/rundeck/integrations-rundeck-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Rundeck tile and input your Rundeck instance Base URL and User API token to grant Infisical access to manage Rundeck keys - - ![integrations rundeck authorization](../../images/integrations/rundeck/integrations-rundeck-auth.png) - - - - Select which Infisical environment secrets you want to sync to a Rundeck Key Storage Path and press create integration to start syncing secrets to Rundeck. - - ![create integration rundeck](../../images/integrations/rundeck/integrations-rundeck-create.png) - ![integrations rundeck](../../images/integrations/rundeck/integrations-rundeck.png) - - - diff --git a/docs/integrations/cloud/teamcity.mdx b/docs/integrations/cicd/teamcity.mdx similarity index 100% rename from docs/integrations/cloud/teamcity.mdx rename to docs/integrations/cicd/teamcity.mdx diff --git a/docs/integrations/cicd/travisci.mdx b/docs/integrations/cicd/travisci.mdx deleted file mode 100644 index 873c371b60..0000000000 --- a/docs/integrations/cicd/travisci.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "Travis CI" -description: "How to sync secrets from Infisical to Travis CI" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain your API token in User Settings > API authentication > Token - - ![integrations travis ci token](../../images/integrations/travis-ci/integrations-travisci-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Travis CI tile and input your Travis CI API token to grant Infisical access to your Travis CI account. - - ![integrations travis ci authorization](../../images/integrations/travis-ci/integrations-travisci-auth.png) - - - - Select which Infisical environment secrets you want to sync to which Travis CI repository and press create integration to start syncing secrets to Travis CI. - - ![create integration travis ci](../../images/integrations/travis-ci/integrations-travisci-create.png) - ![integrations travis ci](../../images/integrations/travis-ci/integrations-travisci.png) - - \ No newline at end of file diff --git a/docs/integrations/cloud/aws-parameter-store.mdx b/docs/integrations/cloud/aws-parameter-store.mdx deleted file mode 100644 index d2bb36a0b8..0000000000 --- a/docs/integrations/cloud/aws-parameter-store.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "AWS Parameter Store" -description: "Learn how to sync secrets from Infisical to AWS Parameter Store." ---- - - - The AWS Parameter Store Native Integration will be deprecated in 2026. Please migrate to our new [AWS Parameter Store Sync](../secret-syncs/aws-parameter-store). - \ No newline at end of file diff --git a/docs/integrations/cloud/aws-secret-manager.mdx b/docs/integrations/cloud/aws-secret-manager.mdx deleted file mode 100644 index a564619983..0000000000 --- a/docs/integrations/cloud/aws-secret-manager.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "AWS Secrets Manager" -description: "Learn how to sync secrets from Infisical to AWS Secrets Manager." ---- - - - The AWS Secrets Manager Native Integration will be deprecated in 2026. Please migrate to our new [AWS Secrets Manager Sync](../secret-syncs/aws-secrets-manager). - \ No newline at end of file diff --git a/docs/integrations/cloud/azure-app-configuration.mdx b/docs/integrations/cloud/azure-app-configuration.mdx deleted file mode 100644 index 4e7dfd94f6..0000000000 --- a/docs/integrations/cloud/azure-app-configuration.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Azure App Configuration" -description: "How to sync secrets from Infisical to Azure App Configuration" ---- - - - The Azure App Configuration Native Integration will be deprecated in 2026. Please migrate to our new [Azure App Configuration Sync](../secret-syncs/azure-app-configuration). - \ No newline at end of file diff --git a/docs/integrations/cloud/azure-devops.mdx b/docs/integrations/cloud/azure-devops.mdx deleted file mode 100644 index 4eaaf0cc17..0000000000 --- a/docs/integrations/cloud/azure-devops.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: "Azure DevOps" -description: "How to sync secrets from Infisical to Azure DevOps" ---- - -### Usage -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com). -- Create a new [Azure DevOps](https://dev.azure.com) project if you don't have one already. - - -#### Create a new Azure DevOps personal access token (PAT) -You'll need to create a new personal access token (PAT) in order to authenticate Infisical with Azure DevOps. - - - ![integrations](../../images/integrations/azure-devops/overview-page.png) - - - Make sure the newly created token has Read/Write access to the Release scope. - ![integrations](../../images/integrations/azure-devops/create-new-token.png) - - - Please make sure that the token has access to the following scopes: Variable Groups _(read, create, & manage)_, Release _(read/write)_, Project and Team _(read)_, Service Connections _(read & query)_ - - - - Copy the newly created token as this will be used to authenticate Infisical with Azure DevOps. - ![integrations](../../images/integrations/azure-devops/new-token-created.png) - - - -#### Setup the Infisical Azure DevOps integration -Navigate to your project's integrations tab and select the 'Azure DevOps' integration. -![integrations](../../images/integrations.png) - - - - Enter your credentials that you obtained from the previous step. - - 1. Azure DevOps API token is the personal access token (PAT) you created in the previous step. - 2. Azure DevOps organization name is the name of your Azure DevOps organization. - - ![integrations](../../images/integrations/azure-devops/new-infiscial-integration-step-1.png) - - - Select Infisical project and secret path you want to sync into Azure DevOps. - Finally, press create integration to start syncing secrets to Azure DevOps. - - ![integrations](../../images/integrations/azure-devops/new-infiscial-integration-step-2.png) - - - -Now you have successfully integrated Infisical with Azure DevOps. Your existing and future secret changes will automatically sync to Azure DevOps. -You can view your secrets by navigating to your Azure DevOps project and selecting the 'Library' tab under 'Pipelines' in the 'Library' section. diff --git a/docs/integrations/cloud/azure-key-vault.mdx b/docs/integrations/cloud/azure-key-vault.mdx deleted file mode 100644 index b0bd80c635..0000000000 --- a/docs/integrations/cloud/azure-key-vault.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Azure Key Vault" -description: "How to sync secrets from Infisical to Azure Key Vault" ---- - - - The Azure Key Vault Native Integration will be deprecated in 2026. Please migrate to our new [Azure Key Vault Sync](../secret-syncs/azure-key-vault). - \ No newline at end of file diff --git a/docs/integrations/cloud/cloud-66.mdx b/docs/integrations/cloud/cloud-66.mdx deleted file mode 100644 index c087f65644..0000000000 --- a/docs/integrations/cloud/cloud-66.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: "Cloud 66" -description: "How to sync secrets from Infisical to Cloud 66" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - -## Navigate to your project's integrations tab - -![integrations](../../images/integrations.png) - -## Enter your Cloud 66 Access Token - -In Cloud 66 Dashboard, click on the top right icon > Account Settings > Access Token -![integrations cloud 66 dashboard](../../images/integrations/cloud-66/integrations-cloud-66-dashboard.png) -![integrations cloud 66 access token](../../images/integrations/cloud-66/integrations-cloud-66-access-token.png) - -Create new Personal Access Token. -![integrations cloud 66 personal access token](../../images/integrations/cloud-66/integrations-cloud-66-pat.png) - -Name it **infisical** and check **Public** and **Admin**. Then click "Create Token" -![integrations cloud 66 personal access token setup](../../images/integrations/cloud-66/integrations-cloud-66-pat-setup.png) - -Copy and save your token. -![integrations cloud 66 copy API token](../../images/integrations/cloud-66/integrations-cloud-66-copy-pat.png) - -### Go to Infisical Integration Page - -Click on the Cloud 66 tile and enter your API token to grant Infisical access to your Cloud 66 account. -![integrations cloud 66 tile in infisical dashboard](../../images/integrations/cloud-66/integrations-cloud-66-infisical-dashboard.png) - -Enter your Cloud 66 Personal Access Token here. Then click "Connect to Cloud 66". -![integrations cloud 66 tile in infisical dashboard](../../images/integrations/cloud-66/integrations-cloud-66-paste-pat.png) - - -## Start integration - -Select which Infisical environment secrets you want to sync to which Cloud 66 stacks and press create integration to start syncing secrets to Cloud 66. -![integrations laravel forge](../../images/integrations/cloud-66/integrations-cloud-66-create.png) - - - Any existing environment variables in Cloud 66 will be deleted when you start syncing. Make sure to add all the secrets into the Infisical dashboard first before doing any integrations. - - -Done! -![integrations laravel forge](../../images/integrations/cloud-66/integrations-cloud-66-done.png) diff --git a/docs/integrations/cloud/cloudflare-pages.mdx b/docs/integrations/cloud/cloudflare-pages.mdx deleted file mode 100644 index addba4fcdf..0000000000 --- a/docs/integrations/cloud/cloudflare-pages.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: "Cloudflare Pages" -description: "How to sync secrets from Infisical to Cloudflare Pages" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Cloudflare [API token](https://dash.cloudflare.com/profile/api-tokens) and [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/): - - Create a new [API token](https://dash.cloudflare.com/profile/api-tokens) in My Profile > API Tokens - - ![integrations cloudflare credentials 1](../../images/integrations/cloudflare/integrations-cloudflare-credentials-1.png) - ![integrations cloudflare credentials 2](../../images/integrations/cloudflare/integrations-cloudflare-credentials-2.png) - ![integrations cloudflare credentials 3](../../images/integrations/cloudflare/integrations-cloudflare-credentials-3.png) - - Copy your [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/) from Account > Workers & Pages > Overview - - ![integrations cloudflare credentials 4](../../images/integrations/cloudflare/integrations-cloudflare-credentials-4.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Cloudflare Pages tile and input your Cloudflare API token and account ID to grant Infisical access to your Cloudflare Pages. - - ![integrations cloudflare authorization](../../images/integrations/cloudflare/integrations-cloudflare-auth.png) - - - - Select which Infisical environment secrets you want to sync to Cloudflare and press create integration to start syncing secrets. - - ![integrations cloudflare](../../images/integrations/cloudflare/integrations-cloudflare-create.png) - ![integrations cloudflare](../../images/integrations/cloudflare/integrations-cloudflare.png) - - \ No newline at end of file diff --git a/docs/integrations/cloud/cloudflare-workers.mdx b/docs/integrations/cloud/cloudflare-workers.mdx deleted file mode 100644 index 10a5797016..0000000000 --- a/docs/integrations/cloud/cloudflare-workers.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: "Cloudflare Workers" -description: "How to sync secrets from Infisical to Cloudflare Workers" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Cloudflare [API token](https://dash.cloudflare.com/profile/api-tokens) and [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/): - - Create a new [API token](https://dash.cloudflare.com/profile/api-tokens) in My Profile > API Tokens - - ![integrations cloudflare credentials 1](../../images/integrations/cloudflare/integrations-cloudflare-credentials-1.png) - ![integrations cloudflare credentials 2](../../images/integrations/cloudflare/integrations-cloudflare-credentials-2.png) - ![integrations cloudflare credentials 3](../../images/integrations/cloudflare/integrations-cloudflare-workers-permission.png) - - Copy your [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/) from Account > Workers & Pages > Overview - - ![integrations cloudflare credentials 4](../../images/integrations/cloudflare/integrations-cloudflare-credentials-4.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Cloudflare Workers tile and input your Cloudflare API token and account ID to grant Infisical access to your Cloudflare Workers. - - ![integrations cloudflare authorization](../../images/integrations/cloudflare/integration-cloudflare-workers-connect.png) - - - - Select which Infisical environment secrets you want to sync to Cloudflare Workers and press create integration to start syncing secrets. - - ![integrations cloudflare](../../images/integrations/cloudflare/integration-cloudflare-workers-create.png) - - - diff --git a/docs/integrations/cloud/databricks.mdx b/docs/integrations/cloud/databricks.mdx deleted file mode 100644 index e5ad229397..0000000000 --- a/docs/integrations/cloud/databricks.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Databricks" -description: "Learn how to sync secrets from Infisical to Databricks." ---- - - - The Databricks Native Integration will be deprecated in 2026. Please migrate to our new [Databricks Sync](../secret-syncs/databricks). - \ No newline at end of file diff --git a/docs/integrations/cloud/digital-ocean-app-platform.mdx b/docs/integrations/cloud/digital-ocean-app-platform.mdx deleted file mode 100644 index a0ed545cc4..0000000000 --- a/docs/integrations/cloud/digital-ocean-app-platform.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: "Digital Ocean App Platform" -description: "How to sync secrets from Infisical to Digital Ocean App Platform" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - -## Get your Digital Ocean Personal Access Tokens - -On Digital Ocean dashboard, navigate to **API > Tokens** and click on "Generate New Token" -![integrations digital ocean dashboard](../../images/integrations/digital-ocean/integrations-do-dashboard.png) - -Name it **infisical**, choose **No expiry**, and make sure to check **Write (optional)**. Then click on "Generate Token" and copy your API token. -![integrations digital ocean token modal](../../images/integrations/digital-ocean/integrations-do-token-modal.png) - -## Navigate to your project's integrations tab - -Click on the **Digital Ocean App Platform** tile and enter your API token to grant Infisical access to your Digital Ocean account. -![integrations](../../images/integrations.png) - -Then enter your Digital Ocean Personal Access Token here. Then click "Connect to Digital Ocean App Platform". -![integrations infisical dashboard digital ocean integration](../../images/integrations/digital-ocean/integrations-do-enter-token.png) - -## Start integration - -Select which Infisical environment secrets you want to sync to which Digital Ocean App and click "Create Integration". -![integrations digital ocean select projects](../../images/integrations/digital-ocean/integrations-do-select-projects.png) - -Done! -![integrations digital ocean integration success](../../images/integrations/digital-ocean/integrations-do-success.png) diff --git a/docs/integrations/cloud/flyio.mdx b/docs/integrations/cloud/flyio.mdx deleted file mode 100644 index 2aa14a9197..0000000000 --- a/docs/integrations/cloud/flyio.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: "Fly.io" -description: "How to sync secrets from Infisical to Fly.io" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Fly.io access token in Access Tokens - - ![integrations fly dashboard](../../images/integrations/flyio/integrations-flyio-dashboard.png) - ![integrations fly token](../../images/integrations/flyio/integrations-flyio-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Fly.io tile and input your Fly.io access token to grant Infisical access to your Fly.io account. - - ![integrations fly authorization](../../images/integrations/flyio/integrations-flyio-auth.png) - - - - Select which Infisical environment secrets you want to sync to which Fly.io app and press create integration to start syncing secrets to Fly.io. - - ![integrations fly](../../images/integrations/flyio/integrations-flyio-create.png) - ![integrations fly](../../images/integrations/flyio/integrations-flyio.png) - - \ No newline at end of file diff --git a/docs/integrations/cloud/gcp-secret-manager.mdx b/docs/integrations/cloud/gcp-secret-manager.mdx deleted file mode 100644 index 22462feefb..0000000000 --- a/docs/integrations/cloud/gcp-secret-manager.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "GCP Secret Manager" -description: "How to sync secrets from Infisical to GCP Secret Manager" ---- - - - The GCP Secret Manager Native Integration will be deprecated in 2026. Please migrate to our new [GCP Secret Manager Sync](../secret-syncs/gcp-secret-manager). - \ No newline at end of file diff --git a/docs/integrations/cloud/hashicorp-vault.mdx b/docs/integrations/cloud/hashicorp-vault.mdx deleted file mode 100644 index df2542ce77..0000000000 --- a/docs/integrations/cloud/hashicorp-vault.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "HashiCorp Vault" -description: "How to sync secrets from Infisical to HashiCorp Vault" ---- - - - The Hashicorp Vault Native Integration will be deprecated in 2026. Please migrate to our new [Hashicorp Vault Sync](../secret-syncs/hashicorp-vault). - diff --git a/docs/integrations/cloud/hasura-cloud.mdx b/docs/integrations/cloud/hasura-cloud.mdx deleted file mode 100644 index f88c1eb508..0000000000 --- a/docs/integrations/cloud/hasura-cloud.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "Hasura Cloud" -description: "How to sync secrets from Infisical to Hasura Cloud" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Hasura Cloud Access Token in My Account > Access Tokens - - ![integrations hasura cloud tokens](../../images/integrations/hasura-cloud/integrations-hasura-cloud-tokens.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Hasura Cloud tile and input your Hasura Cloud access token to grant Infisical access to your Hasura Cloud account. - - ![integrations hasura cloud authorization](../../images/integrations/hasura-cloud/integrations-hasura-cloud-auth.png) - - - - Select which Infisical environment secrets you want to sync to which Hasura Cloud project and press create integration to start syncing secrets to Hasura Cloud. - - ![integrations hasura cloud](../../images/integrations/hasura-cloud/integrations-hasura-cloud-create.png) - ![integrations hasura cloud](../../images/integrations/hasura-cloud/integrations-hasura-cloud.png) - - \ No newline at end of file diff --git a/docs/integrations/cloud/heroku.mdx b/docs/integrations/cloud/heroku.mdx deleted file mode 100644 index 75cf8c106f..0000000000 --- a/docs/integrations/cloud/heroku.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: "Heroku" -description: "How to sync secrets from Infisical to Heroku" ---- - - - - Prerequisites: - - - Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Heroku tile and grant Infisical access to your Heroku account. - - ![integrations heroku authorization](../../images/integrations/heroku/integrations-heroku-auth.png) - - - - Select which Infisical environment secrets you want to sync to which Heroku app and press create integration to start syncing secrets to Heroku. - - ![integrations heroku](../../images/integrations/heroku/integrations-heroku-create.png) - - Here's some guidance on each field: - - - Project Environment: The environment in the current Infisical project from which you want to sync secrets from. - - Secrets Path: The path in the current Infisical project from which you want to sync secrets from such as `/` (for secrets that do not reside in a folder) or `/foo/bar` (for secrets nested in a folder, in this case a folder called `bar` in another folder called `foo`). - - Heroku App: The application in Heroku that you want to sync secrets to. - - Initial Sync Behavior (default is **Import - Prefer values from Infisical**): The behavior of the first sync operation triggered after creating the integration. - - **No Import - Overwrite all values in Heroku**: Sync secrets and overwrite any existing secrets in Heroku. - - **Import - Prefer values from Infisical**: Import secrets from Heroku to Infisical; if a secret with the same name already exists in Infisical, do nothing. Afterwards, sync secrets to Heroku. - - **Import - Prefer values from Heroku**: Import secrets from Heroku to Infisical; if a secret with the same name already exists in Infisical, replace its value with the one from Heroku. Afterwards, sync secrets to Heroku. - - ![integrations heroku](../../images/integrations/heroku/integrations-heroku.png) - - - - - Using the Heroku integration on a self-hosted instance of Infisical requires configuring an API client in Heroku - and registering your instance with it. - - - Navigate to your user Account settings > Applications to create a new API client. - - ![integrations Heroku config](../../images/integrations/heroku/integrations-heroku-config-settings.png) - ![integrations Heroku config](../../images/integrations/heroku/integrations-heroku-config-applications.png) - ![integrations Heroku config](../../images/integrations/heroku/integrations-heroku-config-new-app.png) - - Create the API client. As part of the form, set the **OAuth callback URL** to `https://your-domain.com/integrations/heroku/oauth2/callback`. - - ![integrations Heroku config](../../images/integrations/heroku/integrations-heroku-config-new-app-form.png) - - - Obtain the **Client ID** and **Client Secret** for your Heroku API client. - - ![integrations Heroku config](../../images/integrations/heroku/integrations-heroku-config-credentials.png) - - Back in your Infisical instance, add two new environment variables for the credentials of your Heroku API client. - - - `CLIENT_ID_HEROKU`: The **Client ID** of your Heroku API client. - - `CLIENT_SECRET_HEROKU`: The **Client Secret** of your Heroku API client. - - Once added, restart your Infisical instance and use the Heroku integration. - - - - diff --git a/docs/integrations/cloud/laravel-forge.mdx b/docs/integrations/cloud/laravel-forge.mdx deleted file mode 100644 index c58c4a7be7..0000000000 --- a/docs/integrations/cloud/laravel-forge.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: "Laravel Forge" -description: "How to sync secrets from Infisical to Laravel Forge" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Laravel Forge access token in API Tokens - - ![integrations laravel forge dashboard](../../images/integrations/laravel-forge/integrations-laravelforge-dashboard.png) - ![integrations laravel forge api tokens](../../images/integrations/laravel-forge/integrations-laravelforge-api.png) - - Obtain your Laravel Forge Server ID in Servers > Server ID - - ![integrations laravel forge server](../../images/integrations/laravel-forge/integrations-laravelforge-servers.png) - ![integrations laravel forge server id](../../images/integrations/laravel-forge/integrations-laravelforge-serverid.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Laravel Forge tile and input your Laravel Forge access token and server ID to grant Infisical access to your Laravel Forge account. - - ![integrations laravel forge authorization](../../images/integrations/laravel-forge/integrations-laravelforge-auth.png) - - - - Select which Infisical environment secrets you want to sync to which Laravel Forge site and press create integration to start syncing secrets to Laravel Forge. - - ![integrations laravel forge](../../images/integrations/laravel-forge/integrations-laravelforge-create.png) - ![integrations laravel forge](../../images/integrations/laravel-forge/integrations-laravelforge.png) - - - diff --git a/docs/integrations/cloud/netlify.mdx b/docs/integrations/cloud/netlify.mdx deleted file mode 100644 index f793aae100..0000000000 --- a/docs/integrations/cloud/netlify.mdx +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: "Netlify" -description: "How to sync secrets from Infisical to Netlify" ---- - - - - - Infisical integrates with Netlify's new environment variable experience. If - your site uses Netlify's old environment variable experience, you'll have to - upgrade it to the new one to use this integration. - - - Prerequisites: - - - Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Netlify tile and grant Infisical access to your Netlify account. - - ![integrations netlify authorization](../../images/integrations/netlify/integrations-netlify-auth.png) - - - - Select which Infisical environment secrets you want to sync to which Netlify app and context. Lastly, press create integration to start syncing secrets to Netlify. - - ![integrations netlify](../../images/integrations/netlify/integrations-netlify-create.png) - ![integrations netlify](../../images/integrations/netlify/integrations-netlify.png) - - - - - Using the Netlify integration on a self-hosted instance of Infisical requires configuring an OAuth application in Netlify - and registering your instance with it. - - - Navigate to your User settings > Applications > OAuth to create a new OAuth application. - - ![integrations Netlify config](../../images/integrations/netlify/integrations-netlify-config-user-settings.png) - ![integrations Netlify config](../../images/integrations/netlify/integrations-netlify-config-new-app.png) - - Create the OAuth application. As part of the form, set the **Redirect URI** to `https://your-domain.com/integrations/netlify/oauth2/callback`. - - ![integrations Netlify config](../../images/integrations/netlify/integrations-netlify-config-new-app-form.png) - - - Obtain the **Client ID** and **Secret** for your Netlify OAuth application. - - ![integrations Netlify config](../../images/integrations/netlify/integrations-netlify-config-credentials.png) - - Back in your Infisical instance, add two new environment variables for the credentials of your Netlify OAuth application. - - - `CLIENT_ID_NETLIFY`: The **Client ID** of your Netlify OAuth application. - - `CLIENT_SECRET_NETLIFY`: The **Secret** of your Netlify OAuth application. - - Once added, restart your Infisical instance and use the Netlify integration. - - - - - diff --git a/docs/integrations/cloud/northflank.mdx b/docs/integrations/cloud/northflank.mdx deleted file mode 100644 index 10dcb288eb..0000000000 --- a/docs/integrations/cloud/northflank.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: "Northflank" -description: "How to sync secrets from Infisical to Northflank" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) -- Have a [Northflank](https://northflank.com) project with a secret group ready - - - - Obtain a Northflank API token in Account settings > API > Tokens - - ![integrations northflank dashboard](../../images/integrations/northflank/integrations-northflank-dashboard.png) - ![integrations northflank token](../../images/integrations/northflank/integrations-northflank-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Northflank tile and input your Northflank API token to grant Infisical access to your Northflank account. - - ![integrations northflank authorization](../../images/integrations/northflank/integrations-northflank-auth.png) - - - - Select which Infisical environment secrets you want to sync to which Northflank project and secret group. Finally, press create integration to start syncing secrets to Northflank. - - ![integrations northflank](../../images/integrations/northflank/integrations-northflank-create.png) - ![integrations northflank](../../images/integrations/northflank/integrations-northflank.png) - - \ No newline at end of file diff --git a/docs/integrations/cloud/qovery.mdx b/docs/integrations/cloud/qovery.mdx deleted file mode 100644 index 13aa6af464..0000000000 --- a/docs/integrations/cloud/qovery.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: "Qovery" -description: "How to sync secrets from Infisical to Qovery" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Qovery API Token in Settings > API Token. - - ![integrations qovery api token](../../images/integrations/qovery/integrations-qovery-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Qovery tile and input your Qovery API Token to grant Infisical access to your Qovery account. - - ![integrations qovery authorization](../../images/integrations/qovery/integrations-qovery-auth.png) - - - - Select which Infisical environment secrets you want to sync to Qovery and press create integration to start syncing secrets. - - ![integrations qovery create](../../images/integrations/qovery/integrations-qovery-create-1.png) - - ![integrations qovery create](../../images/integrations/qovery/integrations-qovery-create-2.png) - - - Infisical supports syncing secrets to various Qovery scopes including applications, jobs, or containers. - - - ![integrations qovery settings](../../images/integrations/qovery/integrations-qovery.png) - - diff --git a/docs/integrations/cloud/railway.mdx b/docs/integrations/cloud/railway.mdx deleted file mode 100644 index 77b3155170..0000000000 --- a/docs/integrations/cloud/railway.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: "Railway" -description: "How to sync secrets from Infisical to Railway" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Railway API Token in your Railway [Account Settings > Tokens](https://railway.app/account/tokens). - - ![integrations railway dashboard](../../images/integrations/railway/integrations-railway-dashboard.png) - ![integrations railway token](../../images/integrations/railway/integrations-railway-token.png) - - - If this is your first time creating a Railway API token, then you'll be prompted to join - Railway's Private Boarding Beta program on the Railway Account Settings > Tokens page. - - Note that Railway project tokens will not work for this integration since they don't work with - Railway's Public API. - - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Railway tile and input your Railway API Key to grant Infisical access to your Railway account. - - ![integrations railway authorization](../../images/integrations/railway/integrations-railway-authorization.png) - - - - Select which Infisical environment secrets you want to sync to which Railway project and environment (and optionally service). Lastly, press create integration to start syncing secrets to Railway. - - ![integrations create railway](../../images/integrations/railway/integrations-railway-create.png) - - - Infisical integrates with both Railway's [shared variables](https://blog.railway.app/p/shared-variables-release) at the project environment level as well as service variables at the service level. - - To sync secrets to a specific service in a project, you can select a service from the Railway Service dropdown; otherwise, leaving it empty will sync secrets to the shared variables of that project. - - - ![integrations railway](../../images/integrations/railway/integrations-railway.png) - - \ No newline at end of file diff --git a/docs/integrations/cloud/render.mdx b/docs/integrations/cloud/render.mdx deleted file mode 100644 index 1d4860ebd5..0000000000 --- a/docs/integrations/cloud/render.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "Render" -description: "How to sync secrets from Infisical to Render" ---- - - - The Render Native Integration will be deprecated in 2026. Please migrate to - our new [Render Sync](../secret-syncs/render). - diff --git a/docs/integrations/cloud/supabase.mdx b/docs/integrations/cloud/supabase.mdx deleted file mode 100644 index b5179c45fc..0000000000 --- a/docs/integrations/cloud/supabase.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: "Supabase" -description: "How to sync secrets from Infisical to Supabase" ---- - - - The Supabase integration is useful if your Supabase project uses sensitive-information such as [environment variables in edge functions](https://supabase.com/docs/guides/functions/secrets). - - Synced envars can be accessed in edge functions using Deno's built-in handler: `Deno.env.get(MY_SECRET_NAME)`. - - -Prerequisites: - -- Have an account and project set up at [Supabase](https://supabase.com/) -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Supabase Access Token in your Supabase [Account > Access Tokens](https://app.supabase.com/account/tokens). - ![integrations supabase dashboard](../../images/integrations/supabase/integrations-supabase-dashboard.png) - ![integrations supabase token](../../images/integrations/supabase/integrations-supabase-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Supabase tile and input your Supabase Access Token to grant Infisical access to your Supabase account. - - ![integrations supabase authorization](../../images/integrations/supabase/integrations-supabase-authorization.png) - - - - Select which Infisical environment secrets you want to sync to which Supabase project. Lastly, press create integration to start syncing secrets to Supabase. - - ![integrations supabase create](../../images/integrations/supabase/integrations-supabase-create.png) - - ![integrations supabase](../../images/integrations/supabase/integrations-supabase.png) - - diff --git a/docs/integrations/cloud/terraform-cloud.mdx b/docs/integrations/cloud/terraform-cloud.mdx deleted file mode 100644 index 63398ef4ae..0000000000 --- a/docs/integrations/cloud/terraform-cloud.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Terraform Cloud" -description: "How to sync secrets from Infisical to Terraform Cloud" ---- - - - The Terraform Cloud Native Integration will be deprecated in 2026. Please migrate to our new [Terraform Cloud Sync](../secret-syncs/terraform-cloud). - \ No newline at end of file diff --git a/docs/integrations/cloud/vercel.mdx b/docs/integrations/cloud/vercel.mdx deleted file mode 100644 index 7456776bd6..0000000000 --- a/docs/integrations/cloud/vercel.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Vercel" -description: "How to sync secrets from Infisical to Vercel" ---- - - - The Vercel Native Integration will be deprecated in 2026. Please migrate to our new [Vercel Sync](../secret-syncs/vercel). - \ No newline at end of file diff --git a/docs/integrations/cloud/windmill.mdx b/docs/integrations/cloud/windmill.mdx deleted file mode 100644 index 7d4c2cc82b..0000000000 --- a/docs/integrations/cloud/windmill.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Windmill" -description: "How to sync secrets from Infisical to Windmill" ---- - - - The Windmill Native Integration will be deprecated in 2026. Please migrate to our new [Windmill Sync](../secret-syncs/windmill). - diff --git a/docs/integrations/overview.mdx b/docs/integrations/overview.mdx deleted file mode 100644 index ed7d47b301..0000000000 --- a/docs/integrations/overview.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: "Overview" -description: "How to use Infisical to inject secrets and configs into various 3-rd party services and frameworks." ---- - -Integrations allow environment variables to be synced from Infisical into your local development workflow, CI/CD pipelines, and production infrastructure. - -Missing an integration? [Throw in a request](https://github.com/Infisical/infisical/issues). - -| Integration | Type | Status | -| ------------------------------------------------------------------------------------- | ---------------------- | ---------------------------------- | -| [Docker](/integrations/platforms/docker) | Platform | Available | -| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available | -| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available | -| [Terraform](https://registry.terraform.io/providers/Infisical/infisical/latest/docs) | Infrastructure as code | Available | -| [PM2](/integrations/platforms/pm2) | Platform | Available | -| [Heroku](/integrations/cloud/heroku) | Cloud | Available | -| [Vercel](/integrations/cloud/vercel) | Cloud | Available | -| [Netlify](/integrations/cloud/netlify) | Cloud | Available | -| [Render](/integrations/cloud/render) | Cloud | Available | -| [Laravel Forge](/integrations/cloud/laravel-forge) | Cloud | Available | -| [Railway](/integrations/cloud/railway) | Cloud | Available | -| [Terraform Cloud](/integrations/cloud/terraform-cloud) | Cloud | Available | -| [TeamCity](/integrations/cloud/teamcity) | Cloud | Available | -| [Fly.io](/integrations/cloud/flyio) | Cloud | Available | -| [Supabase](/integrations/cloud/supabase) | Cloud | Available | -| [Northflank](/integrations/cloud/northflank) | Cloud | Available | -| [Cloudflare Pages](/integrations/cloud/cloudflare-pages) | Cloud | Available | -| [Cloudflare Workers](/integrations/cloud/cloudflare-workers) | Cloud | Available | -| [Checkly](/integrations/cloud/checkly) | Cloud | Available | -| [Qovery](/integrations/cloud/qovery) | Cloud | Available | -| [HashiCorp Vault](/integrations/cloud/hashicorp-vault) | Cloud | Available | -| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available | -| [AWS Secrets Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available | -| [Azure Key Vault](/integrations/cloud/azure-key-vault) | Cloud | Available | -| [GCP Secret Manager](/integrations/cloud/gcp-secret-manager) | Cloud | Available | -| [Windmill](/integrations/cloud/windmill) | Cloud | Available | -| [Bitbucket](/integrations/cicd/bitbucket) | CI/CD | Available | -| [Codefresh](/integrations/cicd/codefresh) | CI/CD | Available | -| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available | -| [GitLab](/integrations/cicd/gitlab) | CI/CD | Available | -| [CircleCI](/integrations/cicd/circleci) | CI/CD | Available | -| [Travis CI](/integrations/cicd/travisci) | CI/CD | Available | -| [Rundeck](/integrations/cicd/rundeck) | CI/CD | Available | -| [Octopus Deploy](/integrations/cicd/octopus-deploy) | CI/CD | Available | -| [React](/integrations/frameworks/react) | Framework | Available | -| [Vue](/integrations/frameworks/vue) | Framework | Available | -| [Express](/integrations/frameworks/express) | Framework | Available | -| [Next.js](/integrations/frameworks/nextjs) | Framework | Available | -| [NestJS](/integrations/frameworks/nestjs) | Framework | Available | -| [SvelteKit](/integrations/frameworks/sveltekit) | Framework | Available | -| [Nuxt](/integrations/frameworks/nuxt) | Framework | Available | -| [Gatsby](/integrations/frameworks/gatsby) | Framework | Available | -| [Remix](/integrations/frameworks/remix) | Framework | Available | -| [Vite](/integrations/frameworks/vite) | Framework | Available | -| [Fiber](/integrations/frameworks/fiber) | Framework | Available | -| [Django](/integrations/frameworks/django) | Framework | Available | -| [Flask](/integrations/frameworks/flask) | Framework | Available | -| [Laravel](/integrations/frameworks/laravel) | Framework | Available | -| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available | -| Jenkins | CI/CD | Available | diff --git a/docs/integrations/secret-syncs/overview.mdx b/docs/integrations/secret-syncs/overview.mdx index 937c8d8266..eeadcffa31 100644 --- a/docs/integrations/secret-syncs/overview.mdx +++ b/docs/integrations/secret-syncs/overview.mdx @@ -5,10 +5,6 @@ description: "Learn how to sync secrets to third-party services with Infisical." Secret Syncs enable you to sync secrets from Infisical to third-party services using [App Connections](/integrations/app-connections/overview). - - Secret Syncs will gradually replace Native Integrations as they become available. Native Integrations will be deprecated in the future, so opt for configuring a Secret Sync when available. - - ## Concept Secret Syncs are a project-level resource used to sync secrets, via an [App Connection](/integrations/app-connections/overview), from a particular project environment and folder path (source) @@ -92,7 +88,7 @@ via the UI or API for the third-party service you intend to sync secrets to. Infisical is continuously expanding it's Secret Sync third-party service support. If the service you need isn't available, - you can still use our Native Integrations in the interim, or contact us at team@infisical.com to make a request . + you can contact us at team@infisical.com to make a request . ## Key Schemas diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx index edebf1670c..669c3aaa3c 100644 --- a/docs/self-hosting/configuration/envars.mdx +++ b/docs/self-hosting/configuration/envars.mdx @@ -703,110 +703,6 @@ You can configure third-party app connections for re-use across Infisical Projec -## Native Secret Integrations - -To help you sync secrets from Infisical to services such as Github and Gitlab, Infisical provides native integrations out of the box. - - - - OAuth2 client ID for Heroku integration - - - OAuth2 client secret for Heroku integration - - - - - - OAuth2 client ID for Vercel integration - - -{" "} - - - OAuth2 client secret for Vercel integration - - - - OAuth2 slug for Vercel integration - - - - - - OAuth2 client ID for Netlify integration - - - - OAuth2 client secret for Netlify integration - - - - - - OAuth2 client ID for GitHub integration - - - - OAuth2 client secret for GitHub integration - - - - - - OAuth2 client ID for Bitbucket integration - - - - OAuth2 client secret for Bitbucket integration - - - - - - OAuth2 client id for GCP secrets manager integration - - - - OAuth2 client secret for GCP secrets manager integration - - - - - - The AWS IAM User access key for assuming roles. - - - - The AWS IAM User secret key for assuming roles. - - - - - - OAuth2 client id for Azure integration - - - - OAuth2 client secret for Azure integration - - - - - - OAuth2 client id for Gitlab integration - - - - OAuth2 client secret for Gitlab integration - - - ## Secret Scanning From 5bd8fc9ae49d2a638c3cbdb994052260342b8ef0 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Mon, 24 Nov 2025 20:23:30 -0300 Subject: [PATCH 04/24] fix: correct minor typos in GitLab and AWS Amplify documentation --- docs/integrations/app-connections/gitlab.mdx | 2 +- docs/integrations/cicd/aws-amplify.mdx | 2 +- docs/integrations/secret-syncs/overview.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/integrations/app-connections/gitlab.mdx b/docs/integrations/app-connections/gitlab.mdx index d17322540e..588a6f990d 100644 --- a/docs/integrations/app-connections/gitlab.mdx +++ b/docs/integrations/app-connections/gitlab.mdx @@ -12,7 +12,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access Using the GitLab Connection with OAuth on a self-hosted instance of Infisical requires configuring an OAuth application in GitLab and registering your instance with it. - If you're self-hosting Gitlab with custom certificates, you will have to configure your Infisical instance to trust these certificates. To learn how, please follow [this guide](../../self-hosting/guides/custom-certificates). + If you're self-hosting GitLab with custom certificates, you will have to configure your Infisical instance to trust these certificates. To learn how, please follow [this guide](../../self-hosting/guides/custom-certificates). **Prerequisites:** - A GitLab account with existing projects diff --git a/docs/integrations/cicd/aws-amplify.mdx b/docs/integrations/cicd/aws-amplify.mdx index 9b4b81ccb5..28de7640c2 100644 --- a/docs/integrations/cicd/aws-amplify.mdx +++ b/docs/integrations/cicd/aws-amplify.mdx @@ -19,7 +19,7 @@ This approach enables you to fetch secrets from Infisical during Amplify build t - Create a machine identtiy and connect it to your Infisical project. You can read more about how to use machine identities [here](/documentation/platform/identities/machine-identities). The machine identity will allow you to authenticate and fetch secrets from Infisical. + Create a machine identity and connect it to your Infisical project. You can read more about how to use machine identities [here](/documentation/platform/identities/machine-identities). The machine identity will allow you to authenticate and fetch secrets from Infisical. diff --git a/docs/integrations/secret-syncs/overview.mdx b/docs/integrations/secret-syncs/overview.mdx index eeadcffa31..c341e6ea38 100644 --- a/docs/integrations/secret-syncs/overview.mdx +++ b/docs/integrations/secret-syncs/overview.mdx @@ -88,7 +88,7 @@ via the UI or API for the third-party service you intend to sync secrets to. Infisical is continuously expanding it's Secret Sync third-party service support. If the service you need isn't available, - you can contact us at team@infisical.com to make a request . + you can contact us at team@infisical.com to make a request. ## Key Schemas From d71eac815df73c126e5256452e3bd866c821deb2 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Mon, 24 Nov 2025 22:03:09 -0800 Subject: [PATCH 05/24] feat(dynamic-secrets): retry lease revocation when failing --- .../dynamic-secret-lease-queue.ts | 76 ++++- .../dynamic-secret-lease-service.ts | 6 +- backend/src/lib/delay/index.ts | 10 + backend/src/queue/queue-service.ts | 8 + backend/src/server/routes/index.ts | 5 +- .../platforms/infisical-agent.mdx | 301 ++++++++++++------ .../platforms/kubernetes-injector.mdx | 154 ++++++++- 7 files changed, 434 insertions(+), 126 deletions(-) diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts index 93c3dd1472..48509c65c0 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts @@ -1,10 +1,14 @@ import { DisableRotationErrors } from "@app/ee/services/secret-rotation/secret-rotation-queue"; +import { applyJitter } from "@app/lib/delay"; import { NotFoundError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; +import { TIdentityDALFactory } from "@app/services/identity/identity-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; +import { TSmtpService } from "@app/services/smtp/smtp-service"; +import { TUserDALFactory } from "@app/services/user/user-dal"; import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal"; import { DynamicSecretStatus } from "../dynamic-secret/dynamic-secret-types"; @@ -15,6 +19,9 @@ import { TDynamicSecretLeaseConfig } from "./dynamic-secret-lease-types"; type TDynamicSecretLeaseQueueServiceFactoryDep = { queueService: TQueueServiceFactory; dynamicSecretLeaseDAL: Pick; + smtpService: Pick; + userDAL: Pick; + identityDAL: TIdentityDALFactory; dynamicSecretDAL: Pick; dynamicSecretProviders: Record; kmsService: Pick; @@ -25,6 +32,7 @@ export type TDynamicSecretLeaseQueueServiceFactory = { pruneDynamicSecret: (dynamicSecretCfgId: string) => Promise; setLeaseRevocation: (leaseId: string, expiryAt: Date) => Promise; unsetLeaseRevocation: (leaseId: string) => Promise; + queueFailedRevocation: (leaseId: string) => Promise; init: () => Promise; }; @@ -68,6 +76,20 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, leaseId); }; + const queueFailedRevocation = async (leaseId: string) => { + await queueService.queuePg( + QueueJobs.DynamicSecretRevocation, + { leaseId }, + { + singletonKey: `${leaseId}-retry`, // avoid conflicts with scheduled revocation + retryDelay: Math.floor(applyJitter(3_600_000 * 4) / 1000), // retry every 4 hours with 20% +- jitter + retryLimit: 20, // we dont want it to ever hit the limit, we want the expireInHours to take effect. + expireInHours: 23, // if we set it to 24 hours, pgboss will complain that the expireIn is too high + deadLetter: QueueName.DynamicSecretRevocationFailedRetry // if all fails, we will send a notification to the user + } + ); + }; + const $dynamicSecretQueueJob = async ( jobName: string, jobId: string, @@ -79,7 +101,9 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ logger.info("Dynamic secret lease revocation started: ", leaseId, jobId); const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId); - if (!dynamicSecretLease) throw new DisableRotationErrors({ message: "Dynamic secret lease not found" }); + if (!dynamicSecretLease) { + throw new DisableRotationErrors({ message: "Dynamic secret lease not found" }); + } const folder = await folderDAL.findById(dynamicSecretLease.dynamicSecret.folderId); if (!folder) @@ -150,7 +174,7 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ } logger.info("Finished dynamic secret job", jobId); } catch (error) { - logger.error(error); + logger.error(error, "Failed to delete dynamic secret"); if (jobName === QueueJobs.DynamicSecretPruning) { const { dynamicSecretCfgId } = data as { dynamicSecretCfgId: string }; @@ -166,6 +190,11 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ status: DynamicSecretStatus.FailedDeletion, statusDetails: (error as Error)?.message?.slice(0, 255) }); + + // if revocation fails, we should stop the job and queue a new job to retry the revocation at a later time. + await queueService.stopJobById(QueueName.DynamicSecretRevocation, jobId); + await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, jobId); + await queueFailedRevocation(leaseId); } if (error instanceof DisableRotationErrors) { if (jobId) { @@ -173,7 +202,35 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, jobId); } } - // propogate to next part + } + }; + + // TODO(daniel): add alerting. this is scaffolding for now, pending dashboard overview page for alerts. + const $dynamicSecretRevocationFailedRetryJob = async (jobData: { leaseId: string }, jobId: string) => { + try { + const { leaseId } = jobData; + logger.info({ leaseId, jobId }, "Dynamic secret revocation failed. Notifying root user about failed revocation."); + // const lease = await dynamicSecretLeaseDAL.findById(leaseId); + // if (!lease) { + // throw new DisableRotationErrors({ message: "Dynamic secret lease not found" }); + // } + // const folder = await folderDAL.findById(lease.dynamicSecret.folderId); + // if (!folder) throw new NotFoundError({ message: `Failed to find folder with ${lease.dynamicSecret.folderId}` }); + // + // + // this is where we would send a notification to the user who created the identity, that started the revocation process. + // currently we have no way of knowing which user created the identity, so we cannot send them a notification. + // we shouldn't send an email for EVERY failed revocation. we should have a delay in between, so we don't send spam emails. + // we should have a delay for 2 minutes so we only send out (at most) 1 email every 2 minutes for revocations. + // as an example if 100 failed revocations happen at the same time, we only send out 1 email. + } catch (error) { + if (error instanceof DisableRotationErrors) { + if (jobId) { + await queueService.stopJobById(QueueName.DynamicSecretRevocationFailedRetry, jobId); + await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocationFailedRetry, jobId); + } + } + throw error; } }; @@ -204,12 +261,25 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ pollingIntervalSeconds: 1 } ); + + // this job is triggered when the dead letter queue is triggered from retrying failed lease revocations. + await queueService.startPg( + QueueJobs.DynamicSecretRevocationFailedRetry, + async ([job]) => { + await $dynamicSecretRevocationFailedRetryJob(job.data, job.id); + }, + { + workerCount: 5, + pollingIntervalSeconds: 1 + } + ); }; return { pruneDynamicSecret, setLeaseRevocation, unsetLeaseRevocation, + queueFailedRevocation, init }; }; diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts index cf37626c76..2f1f0a984a 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts @@ -358,11 +358,13 @@ export const dynamicSecretLeaseServiceFactory = ({ if ((revokeResponse as { error?: Error })?.error) { const { error } = revokeResponse as { error?: Error }; logger.error(error?.message, "Failed to revoke lease"); - const deletedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, { + const updatedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, { status: DynamicSecretLeaseStatus.FailedDeletion, statusDetails: error?.message?.slice(0, 255) }); - return deletedDynamicSecretLease; + // queue a job to retry the revocation at a later time + await dynamicSecretQueueService.queueFailedRevocation(dynamicSecretLease.id); + return updatedDynamicSecretLease; } await dynamicSecretQueueService.unsetLeaseRevocation(dynamicSecretLease.id); diff --git a/backend/src/lib/delay/index.ts b/backend/src/lib/delay/index.ts index 32cb8ebfca..a5d4250fcd 100644 --- a/backend/src/lib/delay/index.ts +++ b/backend/src/lib/delay/index.ts @@ -2,3 +2,13 @@ export const delay = (ms: number) => new Promise((resolve) => { setTimeout(resolve, ms); }); + +export const applyJitter = (delayMs: number) => { + const jitterFactor = 0.2; + + // generates random value in [-0.2, +0.2] range + const randomFactor = (Math.random() * 2 - 1) * jitterFactor; + const jitterAmount = randomFactor * delayMs; + + return delayMs + jitterAmount; +}; diff --git a/backend/src/queue/queue-service.ts b/backend/src/queue/queue-service.ts index 8cf8555f99..50fc799f44 100644 --- a/backend/src/queue/queue-service.ts +++ b/backend/src/queue/queue-service.ts @@ -61,6 +61,7 @@ export enum QueueName { SecretPushEventScan = "secret-push-event-scan", UpgradeProjectToGhost = "upgrade-project-to-ghost", DynamicSecretRevocation = "dynamic-secret-revocation", + DynamicSecretRevocationFailedRetry = "dynamic-secret-revocation-failed-retry", CaCrlRotation = "ca-crl-rotation", CaLifecycle = "ca-lifecycle", // parent queue to ca-order-certificate-for-subscriber SecretReplication = "secret-replication", @@ -101,6 +102,7 @@ export enum QueueJobs { UpgradeProjectToGhost = "upgrade-project-to-ghost-job", DynamicSecretRevocation = "dynamic-secret-revocation", DynamicSecretPruning = "dynamic-secret-pruning", + DynamicSecretRevocationFailedRetry = "dynamic-secret-revocation-failed-retry", CaCrlRotation = "ca-crl-rotation-job", SecretReplication = "secret-replication", SecretSync = "secret-sync", // parent queue to push integration sync, webhook, and secret replication @@ -219,6 +221,12 @@ export type TQueueJobTypes = { name: QueueJobs.TelemetryInstanceStats; payload: undefined; }; + [QueueName.DynamicSecretRevocationFailedRetry]: { + name: QueueJobs.DynamicSecretRevocationFailedRetry; + payload: { + leaseId: string; + }; + }; [QueueName.DynamicSecretRevocation]: | { name: QueueJobs.DynamicSecretRevocation; diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 00771168cf..c1070e5973 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -1874,7 +1874,10 @@ export const registerRoutes = async ( dynamicSecretProviders, dynamicSecretDAL, folderDAL, - kmsService + kmsService, + smtpService, + userDAL, + identityDAL }); const dynamicSecretService = dynamicSecretServiceFactory({ projectDAL, diff --git a/docs/integrations/platforms/infisical-agent.mdx b/docs/integrations/platforms/infisical-agent.mdx index 43d322faac..9f485edcc8 100644 --- a/docs/integrations/platforms/infisical-agent.mdx +++ b/docs/integrations/platforms/infisical-agent.mdx @@ -8,12 +8,12 @@ It eliminates the need to modify application logic by enabling clients to decide ![agent diagram](/images/agent/infisical-agent-diagram.png) -### Key features: +## Key Features -- Token renewal: Automatically authenticates with Infisical and deposits renewed access tokens at specified path for applications to consume -- Templating: Renders secrets via user provided templates to desired formats for applications to consume +- **Token lifecycle maangement**: Automatically authenticates with Infisical and deposits renewed access tokens at specified path for applications to consume +- **Templating**: Renders secrets and dynamic secret leases via user provided templates to desired formats for applications to consume -### Token renewal +## Token Renewal The Infisical agent can help manage the life cycle of access tokens. The token renewal process is split into two main components: a `Method`, which is the authentication process suitable for your current setup, and `Sinks`, which are the places where the agent deposits the new access token whenever it receives updates. @@ -28,7 +28,7 @@ Every time the agent successfully retrieves a new access token, it writes the ne to retrieve secrets from Infisical -### Templating +## Templating The Infisical agent can help deliver formatted secrets to your application in a variety of environments. To achieve this, the agent will retrieve secrets from Infisical, format them using a specified template, and then save these formatted secrets to a designated file path. @@ -40,31 +40,203 @@ If this initial attempt is unsuccessful, the agent will momentarily pauses befor Once the agent successfully obtains a valid access token, the agent proceeds to fetch the secrets from Infisical using it. It then formats these secrets using the user provided templates and writes the formatted data to configured file paths. + +### Available secret template functions + +The secret template functions is what you will use to fetch resources such as static secrets and dynamic secret leases from Infisical. Below is a list of the available secret template functions that you can use in your templates. + + + + + ```bash + listSecrets "" "environment-slug" "" "" + ``` + ```bash example-template-usage-1 + {{- with listSecrets "6553ccb2b7da580d7f6e7260" "dev" "/" `{"recursive": false, "expandSecretReferences": true}` }} + {{- range . }} + {{ .Key }}={{ .Value }} + {{- end }} + {{- end }} + ``` + ```bash example-template-usage-2 + {{- with secret "da8056c8-01e2-4d24-b39f-cb4e004b8d44" "staging" "/" `{"recursive": true, "expandSecretReferences": true}` }} + {{- range . }} + {{- if eq .SecretPath "/"}} + {{ .Key }}={{ .Value }} + {{- else}} + {{ .SecretPath }}/{{ .Key }}={{ .Value }} + {{- end}} + {{- end }} + {{- end }} + ``` + + + + **Function name**: listSecrets + + **Description**: This function can be used to render the full list of secrets within a given project, environment and secret path. + + An optional JSON argument is also available. It includes the properties `recursive`, which defaults to false, and `expandSecretReferences`, which defaults to true and expands the returned secrets. + + + **Returns**: A single secret object with the following keys `Key, WorkspaceId, Value, SecretPath, Type, ID, and Comment` + + + + + ```bash + getSecretByName "" "" "" "" + ``` + + ```bash example-template-usage + {{ with getSecretByName "d821f21d-aa90-453b-8448-8c78c1160a0e" "dev" "/" "POSTHOG_HOST"}} + {{ if .Value }} + password = "{{ .Value }}" + {{ end }} + {{ end }} + ``` + + **Function name**: getSecretByName + + **Description**: This function can be used to render a single secret by it's name. + + **Returns**: A list of secret objects with the following keys `Key, WorkspaceId, Value, Type, ID, and Comment` + + + + + ```bash + dynamic_secret "" "" "" "" "" + ``` + + ```bash example-redis-dynamic-secret + {{ with dynamic_secret "aaa-o7en-s5qm" "dev" "/" "redis" "1m" }} + {{ .DB_USERNAME }}={{ .DB_PASSWORD }} + {{- end }} + + ``` + + **Function Name**: dynamic_secret + + **Description**: This function can be used to render a dynamic secret lease credentials. The credentials are automatically renewed before they expire, ensuring that the rendered credentials are always up-to-date. + + **Returns**: An object with keys corresponding to the dynamic secret lease credentials. + + + Note that if you have multiple dynamic secret templates with identical configurations, only one lease will be created in Infisical for those templates, and the same lease will be written to your specified destination paths. + + + + + +## Caching + +The Infisical Agent supports clientside caching of Dynamic Secret leases. If the cache is enabled, the agent will persist the dynamic secret leases to the cache across restarts of the agent. + +### Persistent Caching + +The Agent currently only supports persistent caching. To utilize persistent caching, you must be within a Kubernetes environment. We recommend using the [Infisical Agent Injector](/integrations/platforms/infisical-agent-injector) to inject the agent into pods within your Kubernetes cluster on demand. + +### Cache eviction + +Cache eviction is the process of removing cached data from the cache. The Agent will automatically evict cached data when the cache is full during a garbage collection cycle which is triggered every 10 minutes. + +The cache will automatically evict cached data that has gone stale or is about to go stale. For dynamic resources (such as dynamic secret leases), there's a TTL (Time-to-Live) associated with each lease which is used to determine if the lease is stale or about to go stale. +If a stale dynamic secret lease is detected, it will be automatically evicted from the cache and replaced with a new up-to-date lease. + + +### Cache Configuration + +Configuring the cache is done through the agent configuration file. The following fields are available to configure the cache: + + + + + The type of persistent caching to use. Currently only `kubernetes` is available, and will only work within Kubernetes environments. + + + The path to where your persistent cache will be stored. + + + + Persistent caching is only supported within kubernetes environments at the moment. Please refer to the [Infisical Agent Injector](/integrations/platforms/infisical-agent-injector) documentation for more information on how to use persistent caching within Kubernetes environments. + + + ```yaml example-agent-config-file.yaml + cache: + persistent: + type: "kubernetes" + path: "/home/infisical/cache" + service-account-token-path: "/var/run/secrets/kubernetes.io/serviceaccount/token" + ``` + + + + +## Retrying mechanism + +The agent will automatically attempt to retry failed API requests such as authentication, secrets retrieval, dynamic secret lease provisioning, etc. +By default, the agent will retry up to 3 times with a base delay of 200ms and a maximum delay of 5s. + +You can configure the retrying mechanism through the agent configuration file. The following fields are available to configure the retrying mechanism: + + + + How many times to retry failed API requests such as authentication, secret retrieval, etc. Defaults to `3` retries. + + + The maximum delay between retries. Defaults to `5s` (5 seconds). + + + The base delay between retries. Defaults to `200ms` (200 milliseconds). + + +```yaml example-agent-config-file.yaml +infisical: + address: "https://app.infisical.com" + retry-strategy: + max-retries: 3 + max-delay: "5s" + base-delay: "200ms" + +# ... rest of the agent configuration file +``` + + + ## Agent configuration file To set up the authentication method for token renewal and to define secret templates, the Infisical agent requires a YAML configuration file containing properties defined below. While specifying an authentication method is mandatory to start the agent, configuring sinks and secret templates are optional. -| Field | Description | -| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `infisical.address` | The URL of the Infisical service. Default: `"https://app.infisical.com"`. | -| `infisical.exit-after-auth` | Whether to exit the agent after authentication and first secret render. Default: `"false"`. | -| `infisical.revoke-credentials-on-shutdown` | Whether to revoke all managed dynamic secret leases and identity access tokens on shutdown. Default: `"false"`. | -| `auth.type` | The type of authentication method used. Available options: `universal-auth`, `kubernetes`, `azure`, `gcp-id-token`, `gcp-iam`, `aws-iam` | -| `auth.config.identity-id` | The file path where the machine identity id is stored

This field is required when using any of the following auth types: `kubernetes`, `azure`, `gcp-id-token`, `gcp-iam`, or `aws-iam`. | -| `auth.config.service-account-token` | Path to the Kubernetes service account token to use (optional)

Default: `/var/run/secrets/kubernetes.io/serviceaccount/token` | -| `auth.config.service-account-key` | Path to your GCP service account key file. This field is required when using `gcp-iam` auth type.

Please note that the file should be in JSON format. | -| `auth.config.client-id` | The file path where the universal-auth client id is stored. | -| `auth.config.client-secret` | The file path where the universal-auth client secret is stored. | -| `auth.config.remove_client_secret_on_read` | This will instruct the agent to remove the client secret from disk. | -| `sinks[].type` | The type of sink in a list of sinks. Each item specifies a sink type. Currently, only `"file"` type is available. | -| `sinks[].config.path` | The file path where the access token should be stored for each sink in the list. | -| `templates[].source-path` | The path to the template file that should be used to render secrets. | -| `templates[].template-content` | The inline secret template to be used for rendering the secrets. | -| `templates[].destination-path` | The path where the rendered secrets from the source template will be saved to. | -| `templates[].config.polling-interval` | How frequently to check for secret changes. Default: `5 minutes` (optional) | -| `templates[].config.execute.command` | The command to execute when secret change is detected (optional) | -| `templates[].config.execute.timeout` | How long in seconds to wait for command to execute before timing out (optional) | + + +| Field | Description | +| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `infisical.address` | The URL of the Infisical service. Default: `"https://app.infisical.com"`. | +| `infisical.exit-after-auth` | Whether to exit the agent after authentication and first secret render. Default: `"false"`. | +| `infisical.revoke-credentials-on-shutdown` | Whether to revoke all managed dynamic secret leases and identity access tokens on shutdown. Default: `"false"`. | +| `infisical.retry-strategy.max-retries` | How many times to retry failed API requests such as authentication, secret retrieval, etc. Defaults to `3` retries. | +| `infisical.retry-strategy.max-delay` | The maximum delay between retries. Defaults to `5s` (5 seconds). | +| `infisical.retry-strategy.base-delay` | The base delay between retries. Defaults to `200ms` (200 milliseconds). | +| `auth.type` | The type of authentication method used. Available options: `universal-auth`, `kubernetes`, `azure`, `gcp-id-token`, `gcp-iam`, `aws-iam` | +| `auth.config.identity-id` | The file path where the machine identity id is stored

This field is required when using any of the following auth types: `kubernetes`, `azure`, `gcp-id-token`, `gcp-iam`, or `aws-iam`. | +| `auth.config.service-account-token` | Path to the Kubernetes service account token to use (optional)

Default: `/var/run/secrets/kubernetes.io/serviceaccount/token` | +| `auth.config.service-account-key` | Path to your GCP service account key file. This field is required when using `gcp-iam` auth type.

Please note that the file should be in JSON format. | +| `auth.config.client-id` | The file path where the universal-auth client id is stored. | +| `auth.config.client-secret` | The file path where the universal-auth client secret is stored. | +| `auth.config.remove_client_secret_on_read` | This will instruct the agent to remove the client secret from disk. | +| `sinks[].type` | The type of sink in a list of sinks. Each item specifies a sink type. Currently, only `"file"` type is available. | +| `sinks[].config.path` | The file path where the access token should be stored for each sink in the list. | +| `cache.persistent.type` | The type of persistent caching to use. Currently only `kubernetes` is available, and will only work within Kubernetes environments. | +| `cache.persistent.path` | The path to where your persistent cache will be stored. | +| `cache.persistent.service-account-token-path` | The path to the Kubernetes service account token to use for encrypting the persistent cache. Required when using `kubernetes` cache type. Defaults to `/var/run/secrets/kubernetes.io/serviceaccount/token` | +| `templates[].source-path` | The path to the template file that should be used to render secrets. | +| `templates[].template-content` | The inline secret template to be used for rendering the secrets. | +| `templates[].destination-path` | The path where the rendered secrets from the source template will be saved to. | +| `templates[].config.polling-interval` | How frequently to check for secret changes. Default: `5m` (5 minutes) (optional) | +| `templates[].config.execute.command` | The command to execute when secret change is detected (optional) | +| `templates[].config.execute.timeout` | How long in seconds to wait for command to execute before timing out (optional) | ## Authentication @@ -308,81 +480,4 @@ After defining the agent configuration file, run the command below pointing to t ```bash infisical agent --config example-agent-config-file.yaml -``` - -### Available secret template functions - - - ```bash - listSecrets "" "environment-slug" "" "" - ``` - ```bash example-template-usage-1 - {{- with listSecrets "6553ccb2b7da580d7f6e7260" "dev" "/" `{"recursive": false, "expandSecretReferences": true}` }} - {{- range . }} - {{ .Key }}={{ .Value }} - {{- end }} - {{- end }} - ``` - ```bash example-template-usage-2 -{{- with secret "da8056c8-01e2-4d24-b39f-cb4e004b8d44" "staging" "/" `{"recursive": true, "expandSecretReferences": true}` }} -{{- range . }} -{{- if eq .SecretPath "/"}} -{{ .Key }}={{ .Value }} -{{- else}} -{{ .SecretPath }}/{{ .Key }}={{ .Value }} -{{- end}} -{{- end }} -{{- end }} - ``` - - - -**Function name**: listSecrets - -**Description**: This function can be used to render the full list of secrets within a given project, environment and secret path. - -An optional JSON argument is also available. It includes the properties `recursive`, which defaults to false, and `expandSecretReferences`, which defaults to true and expands the returned secrets. - - -**Returns**: A single secret object with the following keys `Key, WorkspaceId, Value, SecretPath, Type, ID, and Comment` - - - - - ```bash - getSecretByName "" "" "" "" - ``` - -```bash example-template-usage -{{ with getSecretByName "d821f21d-aa90-453b-8448-8c78c1160a0e" "dev" "/" "POSTHOG_HOST"}} -{{ if .Value }} -password = "{{ .Value }}" -{{ end }} -{{ end }} -``` - -**Function name**: getSecretByName - -**Description**: This function can be used to render a single secret by it's name. - -**Returns**: A list of secret objects with the following keys `Key, WorkspaceId, Value, Type, ID, and Comment` - - - - - ```bash - dynamic_secret "" "" "" "" "" - ``` - - ```bash example-redis-dynamic-secret - {{ with dynamic_secret "aaa-o7en-s5qm" "dev" "/" "redis" "1m" }} - {{ .DB_USERNAME }}={{ .DB_PASSWORD }} - {{- end }} - - **Function Name**: dynamic_secret - - **Description**: This function can be used to render a dynamic secret lease credentials. The credentials are automatically renewed before they expire, ensuring that the rendered credentials are always up-to-date. - - **Returns**: An object with keys corresponding to the dynamic secret lease credentials. - ``` - \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/integrations/platforms/kubernetes-injector.mdx b/docs/integrations/platforms/kubernetes-injector.mdx index 9903dcbc32..9e42b912ca 100644 --- a/docs/integrations/platforms/kubernetes-injector.mdx +++ b/docs/integrations/platforms/kubernetes-injector.mdx @@ -120,19 +120,83 @@ You will need to set the `nodeSelector.kubernetes.io/os` label to `windows` and The Infisical Agent Injector supports the following annotations: - - The inject annotation is used to enable the injector on a pod. Set the value to `true` and the pod will be patched with an Infisical Agent container on update or create. - - - The inject mode annotation is used to specify the mode to use to inject the secrets into the pod. + + + The inject annotation is used to enable the injector on a pod. Set the value to `true` and the pod will be patched with an Infisical Agent container on update or create. + + + The inject mode annotation is used to specify the mode to use to inject the secrets into the pod. - - `init`: The init method will create an init container for the pod that will render the secrets into a shared volume mount within the pod. The agent init container will run before any other containers in the pod runs, including other init containers. - - `sidecar`: The sidecar method will create a sidecar container for the pod that will render the secrets into a shared volume mount within the pod. The agent sidecar container will run alongside the main container in the pod. This means that the secrets rendered will always be in sync with your Infisical secrets. - - `sidecar-init`: The sidecar-init method will create the init container and the sidecar container from the other two methods. The init container will run before any other container and fetch the secrets from the start and the sidecar container will keep the secrets in sync throughout the lifecycle of the deployment. - - - The agent config map annotation is used to specify the name of the config map that contains the configuration for the injector. The config map must be in the same namespace as the pod. - + - `init`: The init method will create an init container for the pod that will render the secrets into a shared volume mount within the pod. The agent init container will run before any other containers in the pod runs, including other init containers. + - `sidecar`: The sidecar method will create a sidecar container for the pod that will render the secrets into a shared volume mount within the pod. The agent sidecar container will run alongside the main container in the pod. This means that the secrets rendered will always be in sync with your Infisical secrets. + - `sidecar-init`: The sidecar-init method will create the init container and the sidecar container from the other two methods. The init container will run before any other container and fetch the secrets from the start and the sidecar container will keep the secrets in sync throughout the lifecycle of the deployment. + + + The agent config map annotation is used to specify the name of the config map that contains the configuration for the injector. The config map must be in the same namespace as the pod. + + + + Whether to enable client-side caching of dynamic secret leases. Defaults to `false`. If you set this to `true`, the agent will persist any dynamic secret leases across restarts of the agent. This is especially useful when using the `sidecar-init` inject mode, to pass the dynamic secret leases created in the init container to the sidecar container. + This will ensure that no new leases are created except those initially created in the init container. The sidecar container will register the leases created in the init container and start managing them from that point onwards. + + + + Whether to revoke all managed dynamic secret leases and machine identity access tokens on shutdown. Defaults to `false`. + + If you set this to `true`, all managed dynamic secret leases and machine identity access tokens will be revoked when a `SIGTERM` signal is sent to the agents container _(such as when a pod is terminated or when the pod is restarted)_. + + **Note:** In disaster events such as cluster power outages, a `SIGTERM` signal won't be sent to the agents container, and the credentials will not be revoked. + + + + How many times to retry failed API requests such as authentication, secret retrieval, etc. Defaults to `3` retries. Refer to the [Retrying mechanism](/integrations/platforms/infisical-agent#retrying-mechanism) documentation for more information on how to configure the retry strategy. + + + + The maximum delay between retries. Defaults to `5s` (5 seconds). Refer to the [Retrying mechanism](/integrations/platforms/infisical-agent#retrying-mechanism) documentation for more information on how to configure the retry strategy. + + + + The base delay between retries. Defaults to `200ms` (200 milliseconds). Refer to the [Retrying mechanism](/integrations/platforms/infisical-agent#retrying-mechanism) documentation for more information on how to configure the retry strategy. + + + + The maximum CPU limit for the agent containers. + + Linux Pods: Defaults to `500m` (500 milliCPUs). + Windows Pods: Defaults to `500m` (500 milliCPUs). + + + + The minimum CPU request for the agent containers. + + Linux Pods: Defaults to `100m` (100 milliCPUs). + Windows Pods: Defaults to `100m` (100 milliCPUs). + + + + The maximum memory limit for the agent containers. + + Linux Pods: Defaults to `128Mi` (128 megabytes). + Windows Pods: Defaults to `512Mi` (512 megabytes). + + + + The minimum memory request for the agent containers. + + Linux Pods: Defaults to `64Mi` (64 megabytes). + Windows Pods: Defaults to `256Mi` (256 megabytes). + + + + The maximum ephemeral storage limit for the agent containers. Doesn't have an explicit default vaule. The default value will conform to the default ephemeral storage limit for the pod. + + + + The minimum ephemeral storage request for the agent containers. Doesn't have an explicit default vaule. The default value will conform to the default ephemeral storage request for the pod. + + + ## ConfigMap Configuration @@ -141,18 +205,22 @@ The Infisical Agent Injector supports the following annotations: When you are configuring a pod to use the injector, you must create a config map in the same namespace as the pod you want to inject secrets into. The entire config needs to be of string format and needs to be assigned to the `config.yaml` key in the config map. You can find a full example of the config at the end of this section. + The address of your Infisical instance. This field is optional and will default to `https://app.infisical.com` if not provided. - Whether to revoke all managed dynamic secret leases and identity access tokens on shutdown. Default: `"false"`. + Whether to revoke all managed dynamic secret leases and machine identity access tokens on shutdown. Default: `"false"`. - If this is set to `true`, all managed dynamic secret leases and identity access tokens will be revoked when a `SIGTERM` signal is sent to the agents container _(such as when a pod is terminated or when the pod is restarted)_. + If this is set to `true`, all managed dynamic secret leases and machine identity access tokens will be revoked when a `SIGTERM` signal is sent to the agents container _(such as when a pod is terminated or when the pod is restarted)_. + **Note:** In disaster events such as cluster power outages, a `SIGTERM` signal won't be sent to the agents container, and the credentials will not be revoked. - Note that this is currently unsupported on Windows-based pods, and will only work when injecting into Linux-based pods. + This is currently unsupported on Windows-based pods, and will only work when injecting into Linux-based pods. + + It's recommended to use the annotation `org.infisical.com/agent-revoke-on-shutdown: "true"` instead of configuring the revoke on shutdown on the config map. Refer to the [Supported annotations](/integrations/platforms/kubernetes-injector#supported-annotations) documentation for more information on how to configure the revoke on shutdown through annotations. @@ -162,8 +230,59 @@ The entire config needs to be of string format and needs to be assigned to the ` Please note that the pod's default service account will be used to authenticate with Infisical.
+ - The ID of the machine identity to use to connect to Infisical. This field is required if the `infisical.auth.type` is set to `kubernetes`. + The ID of the machine identity to use for Kubernetes or LDAP authentication. This field is required if the `infisical.auth.type` is set to `kubernetes`. + + + + The LDAP username to use for LDAP authentication. + This field is required if the `infisical.auth.type` is set to `ldap-auth`. + + + + The LDAP password to use for LDAP authentication. + This field is required if the `infisical.auth.type` is set to `ldap-auth`. + + + + How many times to retry failed API requests such as authentication, secret retrieval, etc. Defaults to `3` retries. Refer to the [Retrying mechanism](/integrations/platforms/infisical-agent#retrying-mechanism) documentation for more information on how to configure the retry strategy. + + + You can also configure the max retries through annotations. Refer to the [Supported annotations](/integrations/platforms/kubernetes-injector#supported-annotations) documentation for more information on how to configure the max retries through annotations. + + + + + The maximum delay between retries. Defaults to `5s` (5 seconds). Refer to the [Retrying mechanism](/integrations/platforms/infisical-agent#retrying-mechanism) documentation for more information on how to configure the retry strategy. + + + You can also configure the max delay through annotations. Refer to the [Supported annotations](/integrations/platforms/kubernetes-injector#supported-annotations) documentation for more information on how to configure the max delay through annotations. + + + + + The base delay between retries. Defaults to `200ms` (200 milliseconds). Refer to the [Retrying mechanism](/integrations/platforms/infisical-agent#retrying-mechanism) documentation for more information on how to configure the retry strategy. + + + You can also configure the base delay through annotations. Refer to the [Supported annotations](/integrations/platforms/kubernetes-injector#supported-annotations) documentation for more information on how to configure the base delay through annotations. + + + + + The type of persistent caching to use. Currently only `kubernetes` is available, and will only work within Kubernetes environments. + + + It is recommended to use the annotation `org.infisical.com/agent-cache-enabled: "true"` instead of configuring the cache on the config map. Refer to the [Supported annotations](/integrations/platforms/kubernetes-injector#supported-annotations) documentation for more information on how to configure the cache through annotations. + + + + + The path to the Kubernetes service account token to use for encrypting the persistent cache. Required when using `kubernetes` cache type. Defaults to `/var/run/secrets/kubernetes.io/serviceaccount/token`. + + + It is recommended to use the annotation `org.infisical.com/agent-cache-enabled: "true"` instead of configuring the cache on the config map. Refer to the [Supported annotations](/integrations/platforms/kubernetes-injector#supported-annotations) documentation for more information on how to configure the cache through annotations. + @@ -180,6 +299,7 @@ The templates hold an array of templates that will be rendered and injected into This will be rendered as a [Go Template](https://pkg.go.dev/text/template) and will have access to the following variables. It follows the templating format and supports the same functions as the [Infisical Agent](/integrations/platforms/infisical-agent#quick-start-infisical-agent) + ### Authentication @@ -271,7 +391,7 @@ The Infisical Agent Injector supports Machine Identity [Kubernetes Auth](/docume
-To use the config map in your pod, you will need to add the `org.infisical.com/agent-config-map` annotation to your pod's deployment. The value of the annotation is the name of the config map you created above. +To use the config map in your pod, you will need to add the `org.infisical.com/agent-config-map` annotation to your pod's deployment. The value of the annotation is the name of the config map you created above. The config map must be in the same namespace as the pod you're injecting into. ```yaml apiVersion: v1 kind: Pod From 0b8e39713dd49866a8872ed95cc5eb7d8c1ac97a Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Tue, 25 Nov 2025 10:25:34 -0800 Subject: [PATCH 06/24] Update dynamic-secret-lease-queue.ts --- .../dynamic-secret-lease/dynamic-secret-lease-queue.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts index 48509c65c0..691ba4d00b 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts @@ -82,8 +82,8 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ { leaseId }, { singletonKey: `${leaseId}-retry`, // avoid conflicts with scheduled revocation - retryDelay: Math.floor(applyJitter(3_600_000 * 4) / 1000), // retry every 4 hours with 20% +- jitter - retryLimit: 20, // we dont want it to ever hit the limit, we want the expireInHours to take effect. + retryDelay: Math.floor(applyJitter(3_600_000 * 4) / 1000), // retry every 4 hours with 20% +- jitter (convert ms to seconds for pgboss) + retryLimit: 10, // we dont want it to ever hit the limit, we want the expireInHours to take effect. expireInHours: 23, // if we set it to 24 hours, pgboss will complain that the expireIn is too high deadLetter: QueueName.DynamicSecretRevocationFailedRetry // if all fails, we will send a notification to the user } @@ -192,7 +192,6 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ }); // if revocation fails, we should stop the job and queue a new job to retry the revocation at a later time. - await queueService.stopJobById(QueueName.DynamicSecretRevocation, jobId); await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, jobId); await queueFailedRevocation(leaseId); } From 64ba002af6a774878cfc3163d7fd75bcbd6bf8ae Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Wed, 26 Nov 2025 02:56:40 +0530 Subject: [PATCH 07/24] docs: improves cli docs --- docs/cli/commands/login.mdx | 95 +++++++++++++++++++++++--------- docs/cli/usage.mdx | 106 ++++++++++++++++++++++++++++++------ 2 files changed, 156 insertions(+), 45 deletions(-) diff --git a/docs/cli/commands/login.mdx b/docs/cli/commands/login.mdx index c58c137133..1a0db9b4e6 100644 --- a/docs/cli/commands/login.mdx +++ b/docs/cli/commands/login.mdx @@ -10,6 +10,7 @@ infisical login ### Description The CLI uses authentication to verify your identity. You can authenticate using: + - **Browser Login** (default): Opens a browser for authentication - **Direct Login**: Provide email and password via flags or environment variables for non-interactive workflows - **Interactive CLI Login**: Use the `--interactive` flag to enter credentials via CLI prompts @@ -24,9 +25,9 @@ If you have added multiple users, you can switch between the users by using the **JWT Token Output:** - For **user authentication** with the `--plain --silent` flags: outputs only the JWT access token (useful for scripting) - For **machine identity authentication**: an access token is always printed to the console - + Use the `--plain` flag to print only the token in plain text and the `--silent` flag to disable update alerts. - + Both flags are ideal for capturing the token in environment variables or CI/CD pipelines. @@ -42,29 +43,34 @@ User authentication is designed for individual developers and supports multiple The User authentication method allows you to log in with your email and password. This method supports three different login flows: - - **Browser Login** (default): Opens a browser for authentication - - **Direct Login**: Provide credentials via flags or environment variables for CI/CD - - **Interactive CLI Login**: Enter credentials via CLI prompts using `--interactive` +- **Browser Login** (default): Opens a browser for authentication +- **Direct Login**: Provide credentials via flags or environment variables for CI/CD +- **Interactive CLI Login**: Enter credentials via CLI prompts using `--interactive` - - - - Your email address. Required for direct login along with `--password` and `--organization-id`. - - - Your password. Required for direct login along with `--email` and `--organization-id`. - - - Your organization id. Required for direct login along with `--password` and `--email`. - - - Force interactive CLI login instead of browser-based authentication. - - - Output only the JWT token (useful for scripting and CI/CD). - - - +{" "} + + + + + Your email address. Required for direct login along with `--password` and + `--organization-id`. + + + Your password. Required for direct login along with `--email` and + `--organization-id`. + + + Your organization id. Required for direct login along with `--password` + and `--email`. + + + Force interactive CLI login instead of browser-based authentication. + + + Output only the JWT token (useful for scripting and CI/CD). + + + @@ -291,6 +297,7 @@ Machine identity authentication methods are designed for automated systems, serv ``` + @@ -316,6 +323,7 @@ Machine identity authentication methods are designed for automated systems, serv ``` + @@ -500,6 +508,27 @@ The login command supports a number of flags that you can use for different auth The `jwt` flag can be substituted with the `INFISICAL_JWT` environment variable. + + + ```bash + infisical login --domain= [other-flags] + ``` + + #### Description + Specifies the Infisical API URL for non-US instances (EU Cloud or self-hosted instances). This flag is required when connecting to any instance other than the US Cloud. + + ```bash + # Example for EU Cloud + infisical login --domain="https://eu.infisical.com" --jwt= --machine-identity-id= + + # Example for self-hosted + infisical login --domain="https://your-self-hosted-infisical.com/api" --email user@example.com --password "password" + ``` + + + **Critical:** If you use `--domain` during login, you must also include it on **all subsequent CLI commands** (e.g., `infisical secrets`, `infisical export`, etc.). Alternatively, set the `INFISICAL_API_URL` environment variable to avoid having to use `--domain` on every command. Refer to the [Domain Configuration](/cli/usage#domain-configuration) section for more details. + + @@ -529,8 +558,11 @@ The following examples demonstrate different ways to authenticate as a user with # Basic direct login (defaults to US Cloud) infisical login --email user@example.com --password "your-password" --organization-id "your-organization-id" - # EU Cloud (Custom domain) - infisical login --email user@example.com --password "your-password" --organization-id "your-organization-id" --domain https://eu.infisical.com + # EU Cloud + infisical login --domain https://eu.infisical.com --email user@example.com --password "your-password" --organization-id "your-organization-id" + + # Self-hosted instance + infisical login --domain https://your-self-hosted-infisical.com/api --email user@example.com --password "your-password" --organization-id "your-organization-id" # Output only JWT token for scripting export INFISICAL_TOKEN=$(infisical login --email user@example.com --password "your-password" --organization-id "your-organization-id" --plain --silent) @@ -550,6 +582,11 @@ The following examples demonstrate different ways to authenticate as a user with # Or with plain output for token capture export INFISICAL_TOKEN=$(infisical login --plain --silent) ``` + + + **For non-US instances:** If you're using EU Cloud, or a self-hosted instance, you must set `INFISICAL_API_URL` before login, or use `--domain` on all commands. Refer to the [Domain Configuration](/cli/usage#domain-configuration) section for more details. + + @@ -571,7 +608,7 @@ The following examples demonstrate different ways to authenticate as a user with -If you have SSO enabled, we recommend using the default browser login. + If you have SSO enabled, we recommend using the default browser login. ### Machine Identity Authentication Quick Start @@ -584,6 +621,10 @@ In this example we'll be using the `universal-auth` method to login to obtain an export INFISICAL_TOKEN=$(infisical login --method=universal-auth --client-id= --client-secret= --silent --plain) # silent and plain is important to ensure only the token itself is printed, so we can easily set it as an environment variable. ``` + + **For non-US instances:** If you're using EU Cloud, or a self-hosted instance, you must set `INFISICAL_API_URL` before login, or use `--domain` on all commands. Refer to the [Domain Configuration](/cli/usage#domain-configuration) section for more details. + + Now that we've set the `INFISICAL_TOKEN` environment variable, we can use the CLI to interact with Infisical. The CLI will automatically check for the presence of the `INFISICAL_TOKEN` environment variable and use it for authentication. diff --git a/docs/cli/usage.mdx b/docs/cli/usage.mdx index bedfda22c6..c91ac57368 100644 --- a/docs/cli/usage.mdx +++ b/docs/cli/usage.mdx @@ -131,6 +131,62 @@ For versions prior to v0.4.0, the CLI defaults to the US Cloud. To connect to th + + ## Domain Configuration + +**Important:** If you're not using interactive login, you must configure the domain for **all CLI commands**. + +The CLI defaults to the US Cloud (https://app.infisical.com). To connect to the **EU Cloud (https://eu.infisical.com)** or a **self-hosted instance**, you can configure the domain in one of the following ways: + +- Use the `INFISICAL_API_URL` environment variable +- Use the `--domain` flag on every command + + + + The easiest way to ensure all CLI commands use the correct domain is to set + the `INFISICAL_API_URL` environment variable. This applies the domain + setting globally to all commands: + + ```bash + # Linux/MacOS + export INFISICAL_API_URL="https://your-domain.infisical.com" + + # Windows PowerShell + setx INFISICAL_API_URL "https://your-domain.infisical.com" + ``` + + Once set, all subsequent CLI commands will automatically use this domain: + + ```bash + # Login with the domain + infisical login --method=universal-auth --client-id= --client-secret= --silent --plain + + # All other commands will also use the same domain automatically + infisical secrets --projectId --env dev + ``` + + + + The `--domain` flag can be used to set the domain for a single command. This + applies the domain setting to the command only: + + ```bash + # Login with domain + infisical login --domain="https://your-domain.infisical.com" --method=universal-auth --client-id= --client-secret= --silent --plain + + # All subsequent commands must also include --domain + infisical secrets --domain="https://your-domain.infisical.com" --projectId --env dev + ``` + + + If you use `--domain` during login but forget to include it on subsequent commands, you may encounter authentication errors. + + + + + + + ## Custom Request Headers @@ -186,51 +242,65 @@ For security and privacy concerns, we recommend you to configure your terminal t ## FAQ - - Yes. The CLI is set to connect to Infisical Cloud by default, but if you're running your own instance of Infisical, you can direct the CLI to it using one of the methods provided below. + + Yes. The CLI is set to connect to Infisical US Cloud by default, but if you're using the EU Cloud, a self-hosted instance, you need to configure the domain for **all CLI commands**. - #### Method 1: Use the updated CLI + #### Method 1:Use the updated CLI (v0.4.0+) - Beginning with CLI version V0.4.0, it is now possible to choose between logging in through the Infisical cloud or your own self-hosted instance. Simply execute the `infisical login` command and follow the on-screen instructions. + Beginning with CLI version V0.4.0, you can choose between logging in through the Infisical US Cloud, EU Cloud, or your own self-hosted instance. Simply execute the `infisical login` command and follow the on-screen instructions. - #### Method 2: Export environment variable + #### Method 2: Export environment variable You can point the CLI to the self-hosted Infisical instance by exporting the environment variable `INFISICAL_API_URL` in your terminal. ```bash - # set backend host - export INFISICAL_API_URL="https://your-self-hosted-infisical.com/api" + # Set the API URL + export INFISICAL_API_URL="https://your-self-hosted-infisical.com" - # remove backend host + # For EU Cloud + export INFISICAL_API_URL="https://eu.infisical.com" + + # Remove the setting unset INFISICAL_API_URL ``` ```bash - # set backend host - setx INFISICAL_API_URL "https://your-self-hosted-infisical.com/api" + # Set the API URL + setx INFISICAL_API_URL "https://your-self-hosted-infisical.com" - # remove backend host + # For EU Cloud + setx INFISICAL_API_URL "https://eu.infisical.com" + + # Remove the setting setx INFISICAL_API_URL "" - # NOTE: Once set or removed, please restart powershell for the change to take effect + # NOTE: Once set, please restart powershell for the change to take effect ``` -#### Method 3: Set manually on every command + #### Method 3: Set manually on every command -Another option to point the CLI to your self-hosted Infisical instance is to set it via a flag on every command you run. + If you prefer not to set the environment variable, you must include the `--domain` flag on **every CLI command** you run: -```bash -# Example -infisical --domain="https://your-self-hosted-infisical.com/api" -``` + ```bash + # Login with domain + infisical login --domain="https://your-domain.infisical.com" --method=oidc-auth --jwt $JWT + + # All subsequent commands must also include --domain + infisical secrets --domain="https://your-self-hosted-infisical.com/api" --projectId --env dev + infisical export --domain="https://your-self-hosted-infisical.com/api" --format=dotenv-export + ``` + + + **Best Practice:** Use `INFISICAL_API_URL` environment variable (Method 2) to avoid having to remember the `--domain` flag on every command. This is especially important in CI/CD pipelines and automation scripts. + From dd37fa311439c691a26055e00791e53835c77489 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Tue, 25 Nov 2025 20:26:18 -0300 Subject: [PATCH 08/24] refactor: deprecate native integrations in favor of secret syncs; update API routes and frontend components accordingly --- .../routes/v1/integration-auth-router.ts | 48 ++- .../server/routes/v1/integration-router.ts | 96 ++--- .../endpoints/integrations/create-auth.mdx | 32 -- .../endpoints/integrations/create.mdx | 40 -- .../integrations/delete-auth-by-id.mdx | 4 - .../endpoints/integrations/delete-auth.mdx | 4 - .../endpoints/integrations/delete.mdx | 4 - .../endpoints/integrations/find-auth.mdx | 4 - .../endpoints/integrations/list-auth.mdx | 4 - .../list-project-integrations.mdx | 4 - .../endpoints/integrations/update.mdx | 4 - .../IntegrationsListPage.tsx | 64 +++- .../IntegrationsListPage.utils.tsx | 352 ------------------ .../CloudIntegrationSection.tsx | 258 ------------- .../CloudIntegrationSection/index.tsx | 1 - .../NativeIntegrationsTab.tsx | 105 +----- 16 files changed, 149 insertions(+), 875 deletions(-) delete mode 100644 docs/api-reference/endpoints/integrations/create-auth.mdx delete mode 100644 docs/api-reference/endpoints/integrations/create.mdx delete mode 100644 docs/api-reference/endpoints/integrations/delete-auth-by-id.mdx delete mode 100644 docs/api-reference/endpoints/integrations/delete-auth.mdx delete mode 100644 docs/api-reference/endpoints/integrations/delete.mdx delete mode 100644 docs/api-reference/endpoints/integrations/find-auth.mdx delete mode 100644 docs/api-reference/endpoints/integrations/list-auth.mdx delete mode 100644 docs/api-reference/endpoints/integrations/list-project-integrations.mdx delete mode 100644 docs/api-reference/endpoints/integrations/update.mdx delete mode 100644 frontend/src/pages/secret-manager/IntegrationsListPage/components/CloudIntegrationSection/CloudIntegrationSection.tsx delete mode 100644 frontend/src/pages/secret-manager/IntegrationsListPage/components/CloudIntegrationSection/index.tsx diff --git a/backend/src/server/routes/v1/integration-auth-router.ts b/backend/src/server/routes/v1/integration-auth-router.ts index e5156724de..40bb8fa13e 100644 --- a/backend/src/server/routes/v1/integration-auth-router.ts +++ b/backend/src/server/routes/v1/integration-auth-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { ApiDocsTags, INTEGRATION_AUTH } from "@app/lib/api-docs"; +import { ForbiddenRequestError } from "@app/lib/errors"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -10,6 +11,9 @@ import { Integrations } from "@app/services/integration-auth/integration-list"; import { integrationAuthPubSchema } from "../sanitizedSchemas"; +const NATIVE_INTEGRATION_DEPRECATION_MESSAGE = + "We're moving Native Integrations to Secret Syncs. Check the documentation at https://infisical.com/docs/integrations/secret-syncs/overview. If the integration you need isn't available in the Secret Syncs, please get in touch with us at team@infisical.com."; + export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) => { server.route({ method: "GET", @@ -333,27 +337,33 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) }) } }, - handler: async (req) => { - const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({ - actorId: req.permission.id, - actor: req.permission.type, - actorAuthMethod: req.permission.authMethod, - actorOrgId: req.permission.orgId, - projectId: req.body.workspaceId, - ...req.body + handler: async (_) => { + throw new ForbiddenRequestError({ + message: NATIVE_INTEGRATION_DEPRECATION_MESSAGE }); - await server.services.auditLog.createAuditLog({ - ...req.auditLogInfo, - projectId: req.body.workspaceId, - event: { - type: EventType.AUTHORIZE_INTEGRATION, - metadata: { - integration: integrationAuth.integration - } - } - }); - return { integrationAuth }; + // We are keeping the old response commented out for an easy revert on the API if we need to before the full phase out. + + // const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({ + // actorId: req.permission.id, + // actor: req.permission.type, + // actorAuthMethod: req.permission.authMethod, + // actorOrgId: req.permission.orgId, + // projectId: req.body.workspaceId, + // ...req.body + // }); + + // await server.services.auditLog.createAuditLog({ + // ...req.auditLogInfo, + // projectId: req.body.workspaceId, + // event: { + // type: EventType.AUTHORIZE_INTEGRATION, + // metadata: { + // integration: integrationAuth.integration + // } + // } + // }); + // return { integrationAuth }; } }); diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 95477c3419..183c5a38ba 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -3,17 +3,19 @@ import { z } from "zod"; import { IntegrationsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { ApiDocsTags, INTEGRATION } from "@app/lib/api-docs"; +import { ForbiddenRequestError } from "@app/lib/errors"; import { removeTrailingSlash, shake } from "@app/lib/fn"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; -import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; import { IntegrationMetadataSchema } from "@app/services/integration/integration-schema"; import { Integrations } from "@app/services/integration-auth/integration-list"; -import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telemetry/telemetry-types"; import {} from "../sanitizedSchemas"; +const NATIVE_INTEGRATION_DEPRECATION_MESSAGE = + "We're moving Native Integrations to Secret Syncs. Check the documentation at https://infisical.com/docs/integrations/secret-syncs/overview. If the integration you need isn't available in the Secret Syncs, please get in touch with us at team@infisical.com."; + export const registerIntegrationRouter = async (server: FastifyZodProvider) => { server.route({ method: "POST", @@ -66,52 +68,58 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { } }, onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), - handler: async (req) => { - const { integration, integrationAuth } = await server.services.integration.createIntegration({ - actorId: req.permission.id, - actor: req.permission.type, - actorAuthMethod: req.permission.authMethod, - actorOrgId: req.permission.orgId, - ...req.body + handler: async (_) => { + throw new ForbiddenRequestError({ + message: NATIVE_INTEGRATION_DEPRECATION_MESSAGE }); - const createIntegrationEventProperty = shake({ - integrationId: integration.id.toString(), - integration: integration.integration, - environment: req.body.sourceEnvironment, - secretPath: req.body.secretPath, - url: integration.url, - app: integration.app, - appId: integration.appId, - targetEnvironment: integration.targetEnvironment, - targetEnvironmentId: integration.targetEnvironmentId, - targetService: integration.targetService, - targetServiceId: integration.targetServiceId, - path: integration.path, - region: integration.region - }) as TIntegrationCreatedEvent["properties"]; + // We are keeping the old response commented out for an easy revert on the API if we need to before the full phase out. - await server.services.auditLog.createAuditLog({ - ...req.auditLogInfo, - projectId: integrationAuth.projectId, - event: { - type: EventType.CREATE_INTEGRATION, - // eslint-disable-next-line - metadata: createIntegrationEventProperty - } - }); + // const { integration, integrationAuth } = await server.services.integration.createIntegration({ + // actorId: req.permission.id, + // actor: req.permission.type, + // actorAuthMethod: req.permission.authMethod, + // actorOrgId: req.permission.orgId, + // ...req.body + // }); - await server.services.telemetry.sendPostHogEvents({ - event: PostHogEventTypes.IntegrationCreated, - organizationId: req.permission.orgId, - distinctId: getTelemetryDistinctId(req), - properties: { - ...createIntegrationEventProperty, - projectId: integrationAuth.projectId, - ...req.auditLogInfo - } - }); - return { integration }; + // const createIntegrationEventProperty = shake({ + // integrationId: integration.id.toString(), + // integration: integration.integration, + // environment: req.body.sourceEnvironment, + // secretPath: req.body.secretPath, + // url: integration.url, + // app: integration.app, + // appId: integration.appId, + // targetEnvironment: integration.targetEnvironment, + // targetEnvironmentId: integration.targetEnvironmentId, + // targetService: integration.targetService, + // targetServiceId: integration.targetServiceId, + // path: integration.path, + // region: integration.region + // }) as TIntegrationCreatedEvent["properties"]; + + // await server.services.auditLog.createAuditLog({ + // ...req.auditLogInfo, + // projectId: integrationAuth.projectId, + // event: { + // type: EventType.CREATE_INTEGRATION, + // // eslint-disable-next-line + // metadata: createIntegrationEventProperty + // } + // }); + + // await server.services.telemetry.sendPostHogEvents({ + // event: PostHogEventTypes.IntegrationCreated, + // organizationId: req.permission.orgId, + // distinctId: getTelemetryDistinctId(req), + // properties: { + // ...createIntegrationEventProperty, + // projectId: integrationAuth.projectId, + // ...req.auditLogInfo + // } + // }); + // return { integration }; } }); diff --git a/docs/api-reference/endpoints/integrations/create-auth.mdx b/docs/api-reference/endpoints/integrations/create-auth.mdx deleted file mode 100644 index 5af7a0f9c6..0000000000 --- a/docs/api-reference/endpoints/integrations/create-auth.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: "Create Auth" -openapi: "POST /api/v1/integration-auth/access-token" ---- - -## Integration Authentication Parameters - -The integration authentication endpoint is generic and can be used for all native integrations. -For specific integration parameters for a given service, please review the respective documentation below. - - - - - This value must be **aws-secret-manager**. - - - Infisical project id for the integration. - - - The AWS IAM User Access ID. - - - The AWS IAM User Access Secret Key. - - - - Coming Soon - - - Coming Soon - - diff --git a/docs/api-reference/endpoints/integrations/create.mdx b/docs/api-reference/endpoints/integrations/create.mdx deleted file mode 100644 index 0992e91b9a..0000000000 --- a/docs/api-reference/endpoints/integrations/create.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: "Create" -openapi: "POST /api/v1/integration" ---- - -## Integration Parameters - -The integration creation endpoint is generic and can be used for all native integrations. -For specific integration parameters for a given service, please review the respective documentation below. - - - - - The ID of the integration auth object for authentication with AWS. - Refer [Create Integration Auth](./create-auth) for more info - - - Whether the integration should be active or inactive - - - The secret name used when saving secret in AWS SSM. Used for naming and can be arbitrary. - - - The AWS region of the SSM. Example: `us-east-1` - - - The Infisical environment slug from where secrets will be synced from. Example: `dev` - - - The Infisical folder path from where secrets will be synced from. Example: `/some/path`. The root of the environment is `/`. - - - - Coming Soon - - - Coming Soon - - - diff --git a/docs/api-reference/endpoints/integrations/delete-auth-by-id.mdx b/docs/api-reference/endpoints/integrations/delete-auth-by-id.mdx deleted file mode 100644 index 5884363fc5..0000000000 --- a/docs/api-reference/endpoints/integrations/delete-auth-by-id.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Delete Auth By ID" -openapi: "DELETE /api/v1/integration-auth/{integrationAuthId}" ---- diff --git a/docs/api-reference/endpoints/integrations/delete-auth.mdx b/docs/api-reference/endpoints/integrations/delete-auth.mdx deleted file mode 100644 index 93d9579034..0000000000 --- a/docs/api-reference/endpoints/integrations/delete-auth.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Delete Auth" -openapi: "DELETE /api/v1/integration-auth" ---- diff --git a/docs/api-reference/endpoints/integrations/delete.mdx b/docs/api-reference/endpoints/integrations/delete.mdx deleted file mode 100644 index 51df56de70..0000000000 --- a/docs/api-reference/endpoints/integrations/delete.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Delete" -openapi: "DELETE /api/v1/integration/{integrationId}" ---- diff --git a/docs/api-reference/endpoints/integrations/find-auth.mdx b/docs/api-reference/endpoints/integrations/find-auth.mdx deleted file mode 100644 index 439b829355..0000000000 --- a/docs/api-reference/endpoints/integrations/find-auth.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Get Auth By ID" -openapi: "GET /api/v1/integration-auth/{integrationAuthId}" ---- diff --git a/docs/api-reference/endpoints/integrations/list-auth.mdx b/docs/api-reference/endpoints/integrations/list-auth.mdx deleted file mode 100644 index 3ca961d986..0000000000 --- a/docs/api-reference/endpoints/integrations/list-auth.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "List Auth" -openapi: "GET /api/v1/workspace/{workspaceId}/authorizations" ---- diff --git a/docs/api-reference/endpoints/integrations/list-project-integrations.mdx b/docs/api-reference/endpoints/integrations/list-project-integrations.mdx deleted file mode 100644 index 24ebbf7d85..0000000000 --- a/docs/api-reference/endpoints/integrations/list-project-integrations.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "List Project Integrations" -openapi: "GET /api/v1/workspace/{workspaceId}/integrations" ---- diff --git a/docs/api-reference/endpoints/integrations/update.mdx b/docs/api-reference/endpoints/integrations/update.mdx deleted file mode 100644 index 8567c46aea..0000000000 --- a/docs/api-reference/endpoints/integrations/update.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Update" -openapi: "PATCH /api/v1/integration/{integrationId}" ---- diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx b/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx index bd6ff70d18..4b2d5f750b 100644 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx +++ b/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx @@ -3,7 +3,15 @@ import { useTranslation } from "react-i18next"; import { useNavigate, useSearch } from "@tanstack/react-router"; import { ProjectPermissionCan } from "@app/components/permissions"; -import { PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; +import { + Alert, + AlertDescription, + PageHeader, + Tab, + TabList, + TabPanel, + Tabs +} from "@app/components/v2"; import { ROUTE_PATHS } from "@app/const/routes"; import { ProjectPermissionActions, @@ -12,6 +20,7 @@ import { useProject } from "@app/context"; import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types"; +import { useGetWorkspaceIntegrations } from "@app/hooks/api"; import { ProjectType } from "@app/hooks/api/projects/types"; import { IntegrationsListPageTabs } from "@app/types/integrations"; @@ -32,6 +41,9 @@ export const IntegrationsListPage = () => { from: ROUTE_PATHS.SecretManager.IntegrationsListPage.id }); + const { data: integrations } = useGetWorkspaceIntegrations(currentProject.id); + const hasNativeIntegrations = Boolean(integrations?.length); + const updateSelectedTab = (tab: string) => { navigate({ to: ROUTE_PATHS.SecretManager.IntegrationsListPage.path, @@ -65,9 +77,11 @@ export const IntegrationsListPage = () => { Secret Syncs - - Native Integrations - + {hasNativeIntegrations && ( + + Native Integrations + + )} Framework Integrations @@ -84,15 +98,39 @@ export const IntegrationsListPage = () => { - - - - - + {hasNativeIntegrations && ( + + + + We're moving Native Integrations to{" "} + + Secret Syncs + + . If the integration you need isn't available in the Secret Syncs menu, + please get in touch with us at{" "} + + team@infisical.com + + . + + + + + + + )} diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.utils.tsx b/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.utils.tsx index 9332023f56..bb9d02d3da 100644 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.utils.tsx +++ b/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.utils.tsx @@ -1,10 +1,4 @@ -import crypto from "crypto"; - -import { NavigateFn } from "@tanstack/react-router"; - import { createNotification } from "@app/components/notifications"; -import { localStorageService } from "@app/helpers/localStorage"; -import { TCloudIntegration } from "@app/hooks/api/types"; export const createIntegrationMissingEnvVarsNotification = ( slug: string, @@ -27,349 +21,3 @@ export const createIntegrationMissingEnvVarsNotification = ( ), title: "Missing Environment Variables" }); - -export const redirectForProviderAuth = ( - orgId: string, - projectId: string, - navigate: NavigateFn, - integrationOption: TCloudIntegration -) => { - try { - // generate CSRF token for OAuth2 code-token exchange integrations - const state = crypto.randomBytes(16).toString("hex"); - localStorage.setItem("latestCSRFToken", state); - localStorageService.setIntegrationProjectId(projectId); - - switch (integrationOption.slug) { - case "gcp-secret-manager": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/gcp-secret-manager/authorize", - params: { - orgId, - projectId - } - }); - break; - case "azure-key-vault": { - if (!integrationOption.clientId) { - createIntegrationMissingEnvVarsNotification(integrationOption.slug); - return; - } - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/azure-key-vault/authorize", - params: { - orgId, - projectId - }, - search: { - clientId: integrationOption.clientId, - state - } - }); - break; - } - case "azure-app-configuration": { - if (!integrationOption.clientId) { - createIntegrationMissingEnvVarsNotification(integrationOption.slug); - return; - } - const link = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/azure-app-configuration/oauth2/callback&response_mode=query&scope=https://azconfig.io/.default openid offline_access&state=${state}`; - window.location.assign(link); - break; - } - case "aws-parameter-store": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/aws-parameter-store/authorize", - params: { - orgId, - projectId - } - }); - break; - case "aws-secret-manager": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/aws-secret-manager/authorize", - params: { - orgId, - projectId - } - }); - break; - case "heroku": { - if (!integrationOption.clientId) { - createIntegrationMissingEnvVarsNotification(integrationOption.slug); - return; - } - const link = `https://id.heroku.com/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${state}`; - window.location.assign(link); - break; - } - case "vercel": { - if (!integrationOption.clientSlug) { - createIntegrationMissingEnvVarsNotification(integrationOption.slug); - return; - } - const link = `https://vercel.com/integrations/${integrationOption.clientSlug}/new?state=${state}`; - window.location.assign(link); - break; - } - case "netlify": { - if (!integrationOption.clientId) { - createIntegrationMissingEnvVarsNotification(integrationOption.slug); - return; - } - const link = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/integrations/netlify/oauth2/callback`; - - window.location.assign(link); - break; - } - case "github": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/github/auth-mode-selection", - params: { - orgId, - projectId - } - }); - break; - case "gitlab": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/gitlab/authorize", - params: { - orgId, - projectId - } - }); - break; - case "render": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/render/authorize", - params: { - orgId, - projectId - } - }); - break; - case "flyio": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/flyio/authorize", - params: { - orgId, - projectId - } - }); - break; - case "circleci": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/circleci/authorize", - params: { - orgId, - projectId - } - }); - break; - case "databricks": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/databricks/authorize", - params: { - orgId, - projectId - } - }); - break; - case "laravel-forge": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/laravel-forge/authorize", - params: { - orgId, - projectId - } - }); - break; - case "travisci": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/travisci/authorize", - params: { - orgId, - projectId - } - }); - break; - case "supabase": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/supabase/authorize", - params: { - orgId, - projectId - } - }); - break; - case "checkly": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/checkly/authorize", - params: { - orgId, - projectId - } - }); - break; - case "qovery": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/qovery/authorize", - params: { - orgId, - projectId - } - }); - break; - case "railway": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/railway/authorize", - params: { - orgId, - projectId - } - }); - break; - case "terraform-cloud": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/terraform-cloud/authorize", - params: { - orgId, - projectId - } - }); - break; - case "hashicorp-vault": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/hashicorp-vault/authorize", - params: { - orgId, - projectId - } - }); - break; - case "cloudflare-pages": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/cloudflare-pages/authorize", - params: { - orgId, - projectId - } - }); - break; - case "cloudflare-workers": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/cloudflare-workers/authorize", - params: { - orgId, - projectId - } - }); - break; - case "bitbucket": { - if (!integrationOption.clientId) { - createIntegrationMissingEnvVarsNotification(integrationOption.slug, "cicd"); - return; - } - const link = `https://bitbucket.org/site/oauth2/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/bitbucket/oauth2/callback&state=${state}`; - window.location.assign(link); - break; - } - case "codefresh": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/codefresh/authorize", - params: { - orgId, - projectId - } - }); - break; - case "digital-ocean-app-platform": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/digital-ocean-app-platform/authorize", - params: { - orgId, - projectId - } - }); - break; - case "cloud-66": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/cloud-66/authorize", - params: { - orgId, - projectId - } - }); - break; - case "northflank": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/northflank/authorize", - params: { - orgId, - projectId - } - }); - break; - case "windmill": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/windmill/authorize", - params: { - orgId, - projectId - } - }); - break; - case "teamcity": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/teamcity/authorize", - params: { - orgId, - projectId - } - }); - break; - case "hasura-cloud": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/hasura-cloud/authorize", - params: { - orgId, - projectId - } - }); - break; - case "rundeck": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/rundeck/authorize", - params: { - orgId, - projectId - } - }); - break; - case "azure-devops": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/azure-devops/authorize", - params: { - orgId, - projectId - } - }); - break; - case "octopus-deploy": - navigate({ - to: "/organizations/$orgId/projects/secret-management/$projectId/integrations/octopus-deploy/authorize", - params: { - orgId, - projectId - } - }); - break; - default: - break; - } - } catch (err) { - console.error(err); - } -}; diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/components/CloudIntegrationSection/CloudIntegrationSection.tsx b/frontend/src/pages/secret-manager/IntegrationsListPage/components/CloudIntegrationSection/CloudIntegrationSection.tsx deleted file mode 100644 index ff92f9f413..0000000000 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/components/CloudIntegrationSection/CloudIntegrationSection.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import { useMemo, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { - faCheck, - faChevronLeft, - faMagnifyingGlass, - faSearch, - faXmark -} from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useNavigate } from "@tanstack/react-router"; - -import { NoEnvironmentsBanner } from "@app/components/integrations/NoEnvironmentsBanner"; -import { createNotification } from "@app/components/notifications"; -import { - Button, - DeleteActionModal, - EmptyState, - Input, - Skeleton, - Tooltip -} from "@app/components/v2"; -import { ROUTE_PATHS } from "@app/const/routes"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - useOrganization, - useProject, - useProjectPermission -} from "@app/context"; -import { usePopUp } from "@app/hooks"; -import { SecretSync } from "@app/hooks/api/secretSyncs"; -import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types"; -import { IntegrationsListPageTabs } from "@app/types/integrations"; - -type Props = { - isLoading?: boolean; - integrationAuths?: Record; - cloudIntegrations?: TCloudIntegration[]; - onIntegrationStart: (slug: string) => void; - // cb: handle popUpClose child->parent communication pattern - onIntegrationRevoke: (slug: string, cb: () => void) => void; - onViewActiveIntegrations?: () => void; -}; - -type TRevokeIntegrationPopUp = { provider: string }; - -const SECRET_SYNCS = Object.values(SecretSync) as string[]; -const isSecretSyncAvailable = (type: string) => SECRET_SYNCS.includes(type); - -export const CloudIntegrationSection = ({ - isLoading, - cloudIntegrations = [], - integrationAuths = {}, - onIntegrationStart, - onIntegrationRevoke, - onViewActiveIntegrations -}: Props) => { - const { t } = useTranslation(); - const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - "deleteConfirmation" - ] as const); - const { permission } = useProjectPermission(); - const { currentOrg } = useOrganization(); - const { currentProject } = useProject(); - const navigate = useNavigate(); - - const isEmpty = !isLoading && !cloudIntegrations?.length; - - const sortedCloudIntegrations = useMemo(() => { - const sortedIntegrations = cloudIntegrations.sort((a, b) => a.name.localeCompare(b.name)); - - if (currentProject?.environments.length === 0) { - return sortedIntegrations.map((integration) => ({ ...integration, isAvailable: false })); - } - - return sortedIntegrations; - }, [cloudIntegrations, currentProject?.environments]); - - const [search, setSearch] = useState(""); - - const filteredIntegrations = sortedCloudIntegrations?.filter((cloudIntegration) => - cloudIntegration.name.toLowerCase().includes(search.toLowerCase().trim()) - ); - - return ( -
- {currentProject?.environments.length === 0 && ( -
- -
- )} -
- {onViewActiveIntegrations && ( - - )} -
-
-

{t("integrations.cloud-integrations")}

-

{t("integrations.click-to-start")}

-
- setSearch(e.target.value)} - leftIcon={} - placeholder="Search cloud integrations..." - containerClassName="flex-1 h-min text-base" - /> -
-
-
- {isLoading && - Array.from({ length: 12 }).map((_, index) => ( - - ))} - - {!isLoading && filteredIntegrations.length ? ( - filteredIntegrations.map((cloudIntegration) => { - const syncSlug = cloudIntegration.syncSlug ?? cloudIntegration.slug; - const isSyncAvailable = isSecretSyncAvailable(syncSlug); - - return ( -
null} - role="button" - tabIndex={0} - className={`group relative ${ - cloudIntegration.isAvailable - ? "cursor-pointer duration-200 hover:bg-mineshaft-700" - : "opacity-50" - } flex h-36 flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-3`} - onClick={() => { - if (isSyncAvailable) { - navigate({ - to: ROUTE_PATHS.SecretManager.IntegrationsListPage.path, - params: { - orgId: currentOrg.id, - projectId: currentProject.id - }, - search: { - selectedTab: IntegrationsListPageTabs.SecretSyncs, - addSync: syncSlug as SecretSync - } - }); - return; - } - if (!cloudIntegration.isAvailable) return; - if ( - permission.cannot( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ) - ) { - createNotification({ - type: "error", - text: "You do not have permission to create an integration" - }); - return; - } - onIntegrationStart(cloudIntegration.slug); - }} - key={cloudIntegration.slug} - > -
- integration logo -
- {cloudIntegration.name} -
-
- {cloudIntegration.isAvailable && - Boolean(integrationAuths?.[cloudIntegration.slug]) && ( -
-
-
- - Authorized -
- -
null} - role="button" - tabIndex={0} - onClick={async (event) => { - event.stopPropagation(); - handlePopUpOpen("deleteConfirmation", { - provider: cloudIntegration.slug - }); - }} - className="absolute top-0 right-0 flex h-0 w-12 cursor-pointer items-center justify-center overflow-hidden rounded-r-md bg-red text-xs opacity-50 transition-all duration-300 group-hover:h-full hover:opacity-100" - > - -
-
-
-
- )} - {isSyncAvailable && ( -
-
-
- Secret Sync Available -
-
-
- )} -
- ); - }) - ) : ( - - )} -
- {isEmpty && ( -
- {Array.from({ length: 16 }).map((_, index) => ( -
- ))} -
- )} - handlePopUpToggle("deleteConfirmation", isOpen)} - deleteKey={(popUp?.deleteConfirmation?.data as TRevokeIntegrationPopUp)?.provider || ""} - onDeleteApproved={async () => { - onIntegrationRevoke( - (popUp.deleteConfirmation.data as TRevokeIntegrationPopUp)?.provider, - () => handlePopUpClose("deleteConfirmation") - ); - }} - /> -
- ); -}; diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/components/CloudIntegrationSection/index.tsx b/frontend/src/pages/secret-manager/IntegrationsListPage/components/CloudIntegrationSection/index.tsx deleted file mode 100644 index 62f7a006c4..0000000000 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/components/CloudIntegrationSection/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { CloudIntegrationSection } from "./CloudIntegrationSection"; diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/components/NativeIntegrationsTab/NativeIntegrationsTab.tsx b/frontend/src/pages/secret-manager/IntegrationsListPage/components/NativeIntegrationsTab/NativeIntegrationsTab.tsx index 9670278578..b51c06df42 100644 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/components/NativeIntegrationsTab/NativeIntegrationsTab.tsx +++ b/frontend/src/pages/secret-manager/IntegrationsListPage/components/NativeIntegrationsTab/NativeIntegrationsTab.tsx @@ -1,11 +1,8 @@ -import { useCallback, useEffect, useState } from "react"; -import { faPlus } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useNavigate } from "@tanstack/react-router"; +import { useCallback, useEffect } from "react"; import { createNotification } from "@app/components/notifications"; -import { Button, Checkbox, DeleteActionModal, Spinner } from "@app/components/v2"; -import { useOrganization, useProject } from "@app/context"; +import { Checkbox, DeleteActionModal, Spinner } from "@app/components/v2"; +import { useProject } from "@app/context"; import { usePopUp, useToggle } from "@app/hooks"; import { useDeleteIntegration, @@ -17,38 +14,26 @@ import { import { IntegrationAuth } from "@app/hooks/api/integrationAuth/types"; import { TIntegration } from "@app/hooks/api/integrations/types"; -import { redirectForProviderAuth } from "../../IntegrationsListPage.utils"; -import { CloudIntegrationSection } from "../CloudIntegrationSection"; import { IntegrationsTable } from "./IntegrationsTable"; -enum IntegrationView { - List = "list", - New = "new" -} - export const NativeIntegrationsTab = () => { - const { currentOrg } = useOrganization(); const { currentProject } = useProject(); const { environments, id: workspaceId } = currentProject; - const navigate = useNavigate(); const { data: cloudIntegrations, isPending: isCloudIntegrationsLoading } = useGetCloudIntegrations(); - const { - data: integrationAuths, - isPending: isIntegrationAuthLoading, - isFetching: isIntegrationAuthFetching - } = useGetWorkspaceAuthorizations( - workspaceId, - useCallback((data: IntegrationAuth[]) => { - const groupBy: Record = {}; - data.forEach((el) => { - groupBy[el.integration] = el; - }); - return groupBy; - }, []) - ); + const { data: integrationAuths, isFetching: isIntegrationAuthFetching } = + useGetWorkspaceAuthorizations( + workspaceId, + useCallback((data: IntegrationAuth[]) => { + const groupBy: Record = {}; + data.forEach((el) => { + groupBy[el.integration] = el; + }); + return groupBy; + }, []) + ); // mutation const { @@ -58,11 +43,8 @@ export const NativeIntegrationsTab = () => { } = useGetWorkspaceIntegrations(workspaceId); const { mutateAsync: deleteIntegration } = useDeleteIntegration(); - const { - mutateAsync: deleteIntegrationAuths, - isSuccess: isDeleteIntegrationAuthSuccess, - reset: resetDeleteIntegrationAuths - } = useDeleteIntegrationAuths(); + + const { reset: resetDeleteIntegrationAuths } = useDeleteIntegrationAuths(); const isIntegrationsAuthorizedEmpty = !Object.keys(integrationAuths || {}).length; const isIntegrationsEmpty = !integrations?.length; @@ -71,7 +53,6 @@ export const NativeIntegrationsTab = () => { // After the refetch is completed check if its empty. Then set bot active and reset the submit hook for isSuccess to go back to false useEffect(() => { if ( - isDeleteIntegrationAuthSuccess && !isIntegrationFetching && !isIntegrationAuthFetching && isIntegrationsAuthorizedEmpty && @@ -81,29 +62,11 @@ export const NativeIntegrationsTab = () => { } }, [ isIntegrationFetching, - isDeleteIntegrationAuthSuccess, isIntegrationAuthFetching, isIntegrationsAuthorizedEmpty, isIntegrationsEmpty ]); - const handleProviderIntegration = async (provider: string) => { - const selectedCloudIntegration = cloudIntegrations?.find(({ slug }) => provider === slug); - if (!selectedCloudIntegration) return; - - try { - redirectForProviderAuth(currentOrg.id, currentProject.id, navigate, selectedCloudIntegration); - } catch (error) { - console.error(error); - } - }; - - // function to strat integration for a provider - // confirmation to user passing the bot key for provider to get secret access - const handleProviderIntegrationStart = (provider: string) => { - handleProviderIntegration(provider); - }; - const handleIntegrationDelete = async ( integrationId: string, shouldDeleteIntegrationSecrets: boolean, @@ -117,28 +80,11 @@ export const NativeIntegrationsTab = () => { }); }; - const handleIntegrationAuthRevoke = async (provider: string, cb?: () => void) => { - const integrationAuthForProvider = integrationAuths?.[provider]; - if (!integrationAuthForProvider) return; - - await deleteIntegrationAuths({ - integration: provider, - workspaceId - }); - if (cb) cb(); - createNotification({ - type: "success", - text: "Revoked provider authentication" - }); - }; - const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ "deleteConfirmation", "deleteSecretsConfirmation" ] as const); - const [view, setView] = useState(IntegrationView.List); - const [shouldDeleteSecrets, setShouldDeleteSecrets] = useToggle(false); if (isIntegrationLoading || isCloudIntegrationsLoading) @@ -150,18 +96,10 @@ export const NativeIntegrationsTab = () => { return ( <> - {view === IntegrationView.List ? ( + {integrations?.length && (

Native Integrations

-
{ }} />
- ) : ( - setView(IntegrationView.List)} - /> )} Date: Tue, 25 Nov 2025 15:40:41 -0800 Subject: [PATCH 09/24] feat: alerting for failed revocations --- .../dynamic-secret-lease-queue.ts | 180 ++++++++++++------ .../dynamic-secret-lease-service.ts | 6 +- backend/src/queue/queue-service.ts | 10 +- backend/src/server/routes/index.ts | 4 +- ...micSecretLeaseRevocationFailedTemplate.tsx | 68 +++++++ backend/src/services/smtp/smtp-service.ts | 7 +- .../SecretDashboardPage.tsx | 26 +++ .../DynamicSecretListView.tsx | 17 +- .../SecretDashboardPage/route.tsx | 4 +- 9 files changed, 254 insertions(+), 68 deletions(-) create mode 100644 backend/src/services/smtp/emails/DynamicSecretLeaseRevocationFailedTemplate.tsx diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts index 691ba4d00b..4684ff2b14 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts @@ -1,4 +1,6 @@ +import { ProjectMembershipRole } from "@app/db/schemas"; import { DisableRotationErrors } from "@app/ee/services/secret-rotation/secret-rotation-queue"; +import { getConfig } from "@app/lib/config/env"; import { applyJitter } from "@app/lib/delay"; import { NotFoundError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; @@ -6,8 +8,10 @@ import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; import { TIdentityDALFactory } from "@app/services/identity/identity-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsDataKey } from "@app/services/kms/kms-types"; +import { TProjectDALFactory } from "@app/services/project/project-dal"; +import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; -import { TSmtpService } from "@app/services/smtp/smtp-service"; +import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { TUserDALFactory } from "@app/services/user/user-dal"; import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal"; @@ -22,7 +26,9 @@ type TDynamicSecretLeaseQueueServiceFactoryDep = { smtpService: Pick; userDAL: Pick; identityDAL: TIdentityDALFactory; - dynamicSecretDAL: Pick; + dynamicSecretDAL: Pick; + projectMembershipDAL: Pick; + projectDAL: Pick; dynamicSecretProviders: Record; kmsService: Pick; folderDAL: Pick; @@ -30,9 +36,9 @@ type TDynamicSecretLeaseQueueServiceFactoryDep = { export type TDynamicSecretLeaseQueueServiceFactory = { pruneDynamicSecret: (dynamicSecretCfgId: string) => Promise; - setLeaseRevocation: (leaseId: string, expiryAt: Date) => Promise; + setLeaseRevocation: (leaseId: string, dynamicSecretId: string, expiryAt: Date) => Promise; unsetLeaseRevocation: (leaseId: string) => Promise; - queueFailedRevocation: (leaseId: string) => Promise; + queueFailedRevocation: (leaseId: string, dynamicSecretId: string) => Promise; init: () => Promise; }; @@ -42,7 +48,10 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ dynamicSecretProviders, dynamicSecretLeaseDAL, kmsService, - folderDAL + folderDAL, + projectMembershipDAL, + projectDAL, + smtpService }: TDynamicSecretLeaseQueueServiceFactoryDep): TDynamicSecretLeaseQueueServiceFactory => { const pruneDynamicSecret = async (dynamicSecretCfgId: string) => { await queueService.queuePg( @@ -56,10 +65,10 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ ); }; - const setLeaseRevocation = async (leaseId: string, expiryAt: Date) => { + const setLeaseRevocation = async (leaseId: string, dynamicSecretId: string, expiryAt: Date) => { await queueService.queuePg( QueueJobs.DynamicSecretRevocation, - { leaseId }, + { leaseId, dynamicSecretId }, { id: leaseId, singletonKey: leaseId, @@ -76,16 +85,39 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, leaseId); }; - const queueFailedRevocation = async (leaseId: string) => { + const queueFailedRevocation = async (leaseId: string, dynamicSecretId: string) => { + const appConfig = getConfig(); + + const retryDelaySeconds = appConfig.isDevelopmentMode ? 1 : Math.floor(applyJitter(3_600_000 * 4) / 1000); // retry every 4 hours with 20% +- jitter (convert ms to seconds for pgboss) + await queueService.queuePg( QueueJobs.DynamicSecretRevocation, - { leaseId }, + { leaseId, isRetry: true, dynamicSecretId }, { singletonKey: `${leaseId}-retry`, // avoid conflicts with scheduled revocation - retryDelay: Math.floor(applyJitter(3_600_000 * 4) / 1000), // retry every 4 hours with 20% +- jitter (convert ms to seconds for pgboss) + retryDelay: retryDelaySeconds, retryLimit: 10, // we dont want it to ever hit the limit, we want the expireInHours to take effect. - expireInHours: 23, // if we set it to 24 hours, pgboss will complain that the expireIn is too high - deadLetter: QueueName.DynamicSecretRevocationFailedRetry // if all fails, we will send a notification to the user + expireInHours: 23 // if we set it to 24 hours, pgboss will complain that the expireIn is too high + } + ); + }; + + const $queueDynamicSecretLeaseRevocationFailedEmail = async (leaseId: string, dynamicSecretId: string) => { + await queueService.queue( + QueueName.DynamicSecretLeaseRevocationFailedEmail, + QueueJobs.DynamicSecretLeaseRevocationFailedEmail, + { + leaseId + }, + { + jobId: `dynamic-secret-lease-revocation-failed-email-${dynamicSecretId}`, + delay: 1000 * 60, // 1 minute + backoff: { + type: "exponential", + delay: 1000 * 60 // 1 minute + }, + removeOnComplete: true, + removeOnFail: true } ); }; @@ -93,7 +125,8 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ const $dynamicSecretQueueJob = async ( jobName: string, jobId: string, - data: { leaseId: string } | { dynamicSecretCfgId: string } + data: { leaseId: string; dynamicSecretId: string; isRetry?: boolean } | { dynamicSecretCfgId: string }, + retryCount?: number ): Promise => { try { if (jobName === QueueJobs.DynamicSecretRevocation) { @@ -185,52 +218,92 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ } if (jobName === QueueJobs.DynamicSecretRevocation) { - const { leaseId } = data as { leaseId: string }; + const { leaseId, isRetry, dynamicSecretId } = data as { + leaseId: string; + isRetry?: boolean; + dynamicSecretId: string; + }; await dynamicSecretLeaseDAL.updateById(leaseId, { status: DynamicSecretStatus.FailedDeletion, - statusDetails: (error as Error)?.message?.slice(0, 255) + statusDetails: `${(error as Error)?.message?.slice(0, 255)} - Retrying automatically` }); - // if revocation fails, we should stop the job and queue a new job to retry the revocation at a later time. - await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, jobId); - await queueFailedRevocation(leaseId); + // only add to retry queue if this is not a retry, and if the error is not a DisableRotationErrors error + if (!isRetry && !(error instanceof DisableRotationErrors)) { + // if revocation fails, we should stop the job and queue a new job to retry the revocation at a later time. + await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, jobId); + await queueService.stopRepeatableJobByJobId(QueueName.DynamicSecretRevocation, jobId); + await queueFailedRevocation(leaseId, dynamicSecretId); + } else if (isRetry && !(error instanceof DisableRotationErrors)) { + if (retryCount && retryCount === 10) { + await $queueDynamicSecretLeaseRevocationFailedEmail(leaseId, dynamicSecretId); + } + } } if (error instanceof DisableRotationErrors) { if (jobId) { await queueService.stopRepeatableJobByJobId(QueueName.DynamicSecretRevocation, jobId); await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, jobId); } + } else { + // propagate to next part + throw error; } } }; - // TODO(daniel): add alerting. this is scaffolding for now, pending dashboard overview page for alerts. - const $dynamicSecretRevocationFailedRetryJob = async (jobData: { leaseId: string }, jobId: string) => { + const $dynamicSecretLeaseRevocationFailedEmailJob = async (jobId: string, data: { leaseId: string }) => { try { - const { leaseId } = jobData; - logger.info({ leaseId, jobId }, "Dynamic secret revocation failed. Notifying root user about failed revocation."); - // const lease = await dynamicSecretLeaseDAL.findById(leaseId); - // if (!lease) { - // throw new DisableRotationErrors({ message: "Dynamic secret lease not found" }); - // } - // const folder = await folderDAL.findById(lease.dynamicSecret.folderId); - // if (!folder) throw new NotFoundError({ message: `Failed to find folder with ${lease.dynamicSecret.folderId}` }); - // - // - // this is where we would send a notification to the user who created the identity, that started the revocation process. - // currently we have no way of knowing which user created the identity, so we cannot send them a notification. - // we shouldn't send an email for EVERY failed revocation. we should have a delay in between, so we don't send spam emails. - // we should have a delay for 2 minutes so we only send out (at most) 1 email every 2 minutes for revocations. - // as an example if 100 failed revocations happen at the same time, we only send out 1 email. - } catch (error) { - if (error instanceof DisableRotationErrors) { - if (jobId) { - await queueService.stopJobById(QueueName.DynamicSecretRevocationFailedRetry, jobId); - await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocationFailedRetry, jobId); - } + const appCfg = getConfig(); + + const { leaseId } = data; + logger.info( + { leaseId, jobId }, + "Dynamic secret revocation failed. Notifying project admins about failed revocation." + ); + + const lease = await dynamicSecretLeaseDAL.findById(leaseId); + if (!lease) { + throw new DisableRotationErrors({ message: "Dynamic secret lease not found" }); } - throw error; + const dynamicSecret = await dynamicSecretDAL.findOne({ id: lease.dynamicSecretId }); + if (!dynamicSecret) { + throw new DisableRotationErrors({ message: "Dynamic secret not found" }); + } + + const folder = await folderDAL.findById(lease.dynamicSecret.folderId); + if (!folder) throw new NotFoundError({ message: `Failed to find folder with ${lease.dynamicSecret.folderId}` }); + + const project = await projectDAL.findById(folder.projectId); + const projectMembers = await projectMembershipDAL.findAllProjectMembers(project.id); + + const projectAdmins = projectMembers.filter((member) => + member.roles.some((role) => role.role === ProjectMembershipRole.Admin) + ); + + await smtpService.sendMail({ + recipients: projectAdmins.map((member) => member.user.email!).filter(Boolean), + template: SmtpTemplates.DynamicSecretLeaseRevocationFailed, + subjectLine: "Dynamic Secret Lease Revocation Failed", + substitutions: { + dynamicSecretLeaseUrl: `${appCfg.SITE_URL}/organizations/${project.orgId}/projects/secret-management/${project.id}/secrets/${folder.environment.envSlug}?dynamicSecretId=${lease.dynamicSecret.id}&filterBy=dynamic&search=${dynamicSecret.name}`, + dynamicSecretName: lease.dynamicSecret.name, + projectName: project.name, + environmentSlug: folder.environment.envSlug, + errorMessage: lease.statusDetails || "An unknown error occurred" + } + }); + } catch (error) { + logger.error(error, "Failed to send dynamic secret lease revocation failed email"); + if (error instanceof DisableRotationErrors) { + if (jobId) { + await queueService.stopRepeatableJobByJobId(QueueName.DynamicSecretLeaseRevocationFailedEmail, jobId); + await queueService.stopJobById(QueueName.DynamicSecretLeaseRevocationFailedEmail, jobId); + } + } else { + throw error; + } } }; @@ -238,14 +311,21 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ await $dynamicSecretQueueJob(job.name, job.id as string, job.data); }); + // we use redis for sending the email because: + // 1. we are insensitive to losing the jobs in queue in case of a disaster event + // 2. pgboss does not support exclusive job keys on v0.10.x, and upgrading to v0.11.x which supports exclusive jobs comes with a lot of breaking changes, and we would need to manually migrate our existing jobs to the new version + queueService.start(QueueName.DynamicSecretLeaseRevocationFailedEmail, async (job) => { + await $dynamicSecretLeaseRevocationFailedEmailJob(job.id as string, job.data); + }); + const init = async () => { await queueService.startPg( QueueJobs.DynamicSecretRevocation, async ([job]) => { - await $dynamicSecretQueueJob(job.name, job.id, job.data); + await $dynamicSecretQueueJob(job.name, job.id, job.data, job.retryCount); }, { - workerCount: 5, + workerCount: 10, pollingIntervalSeconds: 1 } ); @@ -260,18 +340,6 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ pollingIntervalSeconds: 1 } ); - - // this job is triggered when the dead letter queue is triggered from retrying failed lease revocations. - await queueService.startPg( - QueueJobs.DynamicSecretRevocationFailedRetry, - async ([job]) => { - await $dynamicSecretRevocationFailedRetryJob(job.data, job.id); - }, - { - workerCount: 5, - pollingIntervalSeconds: 1 - } - ); }; return { diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts index 2f1f0a984a..ea5efd5024 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts @@ -178,7 +178,7 @@ export const dynamicSecretLeaseServiceFactory = ({ config }); - await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, expireAt); + await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, dynamicSecretCfg.id, expireAt); return { lease: dynamicSecretLease, dynamicSecret: dynamicSecretCfg, data }; }; @@ -272,7 +272,7 @@ export const dynamicSecretLeaseServiceFactory = ({ ); await dynamicSecretQueueService.unsetLeaseRevocation(dynamicSecretLease.id); - await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, expireAt); + await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, dynamicSecretCfg.id, expireAt); const updatedDynamicSecretLease = await dynamicSecretLeaseDAL.updateById(dynamicSecretLease.id, { expireAt, externalEntityId: entityId @@ -363,7 +363,7 @@ export const dynamicSecretLeaseServiceFactory = ({ statusDetails: error?.message?.slice(0, 255) }); // queue a job to retry the revocation at a later time - await dynamicSecretQueueService.queueFailedRevocation(dynamicSecretLease.id); + await dynamicSecretQueueService.queueFailedRevocation(dynamicSecretLease.id, dynamicSecretCfg.id); return updatedDynamicSecretLease; } diff --git a/backend/src/queue/queue-service.ts b/backend/src/queue/queue-service.ts index 50fc799f44..57409d173b 100644 --- a/backend/src/queue/queue-service.ts +++ b/backend/src/queue/queue-service.ts @@ -61,7 +61,7 @@ export enum QueueName { SecretPushEventScan = "secret-push-event-scan", UpgradeProjectToGhost = "upgrade-project-to-ghost", DynamicSecretRevocation = "dynamic-secret-revocation", - DynamicSecretRevocationFailedRetry = "dynamic-secret-revocation-failed-retry", + DynamicSecretLeaseRevocationFailedEmail = "dynamic-secret-lease-revocation-failed-email", CaCrlRotation = "ca-crl-rotation", CaLifecycle = "ca-lifecycle", // parent queue to ca-order-certificate-for-subscriber SecretReplication = "secret-replication", @@ -102,7 +102,6 @@ export enum QueueJobs { UpgradeProjectToGhost = "upgrade-project-to-ghost-job", DynamicSecretRevocation = "dynamic-secret-revocation", DynamicSecretPruning = "dynamic-secret-pruning", - DynamicSecretRevocationFailedRetry = "dynamic-secret-revocation-failed-retry", CaCrlRotation = "ca-crl-rotation-job", SecretReplication = "secret-replication", SecretSync = "secret-sync", // parent queue to push integration sync, webhook, and secret replication @@ -122,6 +121,7 @@ export enum QueueJobs { SecretRotationV2RotateSecrets = "secret-rotation-v2-rotate-secrets", SecretRotationV2SendNotification = "secret-rotation-v2-send-notification", CreateFolderTreeCheckpoint = "create-folder-tree-checkpoint", + DynamicSecretLeaseRevocationFailedEmail = "dynamic-secret-lease-revocation-failed-email", InvalidateCache = "invalidate-cache", SecretScanningV2FullScan = "secret-scanning-v2-full-scan", SecretScanningV2DiffScan = "secret-scanning-v2-diff-scan", @@ -221,8 +221,8 @@ export type TQueueJobTypes = { name: QueueJobs.TelemetryInstanceStats; payload: undefined; }; - [QueueName.DynamicSecretRevocationFailedRetry]: { - name: QueueJobs.DynamicSecretRevocationFailedRetry; + [QueueName.DynamicSecretLeaseRevocationFailedEmail]: { + name: QueueJobs.DynamicSecretLeaseRevocationFailedEmail; payload: { leaseId: string; }; @@ -231,7 +231,9 @@ export type TQueueJobTypes = { | { name: QueueJobs.DynamicSecretRevocation; payload: { + isRetry?: boolean; leaseId: string; + dynamicSecretId: string; }; } | { diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index c1070e5973..860912934e 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -1877,7 +1877,9 @@ export const registerRoutes = async ( kmsService, smtpService, userDAL, - identityDAL + identityDAL, + projectMembershipDAL, + projectDAL }); const dynamicSecretService = dynamicSecretServiceFactory({ projectDAL, diff --git a/backend/src/services/smtp/emails/DynamicSecretLeaseRevocationFailedTemplate.tsx b/backend/src/services/smtp/emails/DynamicSecretLeaseRevocationFailedTemplate.tsx new file mode 100644 index 0000000000..3cb982f646 --- /dev/null +++ b/backend/src/services/smtp/emails/DynamicSecretLeaseRevocationFailedTemplate.tsx @@ -0,0 +1,68 @@ +import { Heading, Section, Text } from "@react-email/components"; + +import { BaseButton } from "./BaseButton"; +import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper"; + +interface DynamicSecretLeaseRevocationFailedTemplateProps + extends Omit { + siteUrl: string; + dynamicSecretLeaseUrl: string; + dynamicSecretName: string; + projectName: string; + environmentSlug: string; + errorMessage: string; +} + +export const DynamicSecretLeaseRevocationFailedTemplate = ({ + siteUrl, + dynamicSecretLeaseUrl, + dynamicSecretName, + projectName, + environmentSlug, + errorMessage +}: DynamicSecretLeaseRevocationFailedTemplateProps) => { + return ( + + + Dynamic Secret Lease Revocation Failed + +
+ + One or more leases for the dynamic secret {dynamicSecretName} in project{" "} + {projectName} and environment {environmentSlug} have failed to revoke after + multiple attempts. + + + Please review the dynamic secret lease and attempt to revoke it again. + +
+ +
+ + Latest error message + + {errorMessage} +
+ +
+ View Dynamic Secret Leases +
+
+ ); +}; + +export default DynamicSecretLeaseRevocationFailedTemplate; + +DynamicSecretLeaseRevocationFailedTemplate.PreviewProps = { + errorMessage: 'REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM "[REDACTED]" - tuple concurrently updated.', + dynamicSecretLeaseUrl: "https://infisical.com/test", + leaseId: "717d5013-7194-49d9-b6ac-6192328c2914", + dynamicSecretName: "postgres-prod-db", + projectName: "Development Team", + environmentSlug: "dev", + siteUrl: "https://infisical.com" +} as DynamicSecretLeaseRevocationFailedTemplateProps; diff --git a/backend/src/services/smtp/smtp-service.ts b/backend/src/services/smtp/smtp-service.ts index cef22009ad..62906764fd 100644 --- a/backend/src/services/smtp/smtp-service.ts +++ b/backend/src/services/smtp/smtp-service.ts @@ -43,6 +43,7 @@ import { SubOrganizationInvitationTemplate, UnlockAccountTemplate } from "./emails"; +import DynamicSecretLeaseRevocationFailedTemplate from "./emails/DynamicSecretLeaseRevocationFailedTemplate"; export type TSmtpConfig = SMTPTransport.Options; export type TSmtpSendMail = { @@ -89,7 +90,8 @@ export enum SmtpTemplates { SecretScanningV2ScanFailed = "secretScanningV2ScanFailed", SecretScanningV2SecretsDetected = "secretScanningV2SecretsDetected", AccountDeletionConfirmation = "accountDeletionConfirmation", - HealthAlert = "healthAlert" + HealthAlert = "healthAlert", + DynamicSecretLeaseRevocationFailed = "dynamicSecretLeaseRevocationFailed" } export enum SmtpHost { @@ -137,7 +139,8 @@ const EmailTemplateMap: Record> = { [SmtpTemplates.SecretScanningV2ScanFailed]: SecretScanningScanFailedTemplate, [SmtpTemplates.SecretScanningV2SecretsDetected]: SecretScanningSecretsDetectedTemplate, [SmtpTemplates.AccountDeletionConfirmation]: AccountDeletionConfirmationTemplate, - [SmtpTemplates.HealthAlert]: HealthAlertTemplate + [SmtpTemplates.HealthAlert]: HealthAlertTemplate, + [SmtpTemplates.DynamicSecretLeaseRevocationFailed]: DynamicSecretLeaseRevocationFailedTemplate }; export const smtpServiceFactory = (cfg: TSmtpConfig) => { diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx index 8e061291d9..77ea94f993 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx @@ -121,6 +121,9 @@ const Page = () => { const tableRef = useRef(null); const [isVisible, setIsVisible] = useState(false); + const [selectedDynamicSecretId, setSelectedDynamicSecretId] = useState( + routerQueryParams.dynamicSecretId || "" + ); const { isBatchMode, pendingChanges } = useBatchMode(); const { loadPendingChanges, setExistingKeys } = useBatchModeActions(); @@ -165,6 +168,28 @@ const Page = () => { if (isVisible) setIsVisible(false); }, [environment]); + useEffect(() => { + if (routerQueryParams.dynamicSecretId !== null) { + setSelectedDynamicSecretId(routerQueryParams.dynamicSecretId); + + navigate({ + search: (prev) => ({ + ...prev, + dynamicSecretId: undefined + }) + }); + + // if any of the router query params are changed, we have to clear the selected dynamic secret id to avoid re-rendering the lease modal when it suddendly becomes available + } else { + setSelectedDynamicSecretId(null); + } + }, [ + routerQueryParams.filterBy, + routerQueryParams.search, + routerQueryParams.secretPath, + routerQueryParams.tags + ]); + const canReadSecret = hasSecretReadValueOrDescribePermission( permission, ProjectPermissionSecretActions.DescribeSecret, @@ -1039,6 +1064,7 @@ const Page = () => { )} {canReadDynamicSecret && Boolean(dynamicSecrets?.length) && ( { + if (selectedDynamicSecretId) { + handlePopUpOpen("dynamicSecretLeases", selectedDynamicSecretId); + } + }, [selectedDynamicSecretId]); + return ( <> {dynamicSecrets.map((secret) => { @@ -231,7 +241,12 @@ export const DynamicSecretListView = ({
+

Dynamic secret leases

+ {secret.name} + + } subTitle="Revoke or renew your secret leases" className="max-w-3xl" > diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/route.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/route.tsx index f5796fd693..df054b6e2f 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/route.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/route.tsx @@ -12,6 +12,7 @@ const SecretDashboardPageQueryParamsSchema = z.object({ search: z.string().catch(""), tags: z.string().catch(""), filterBy: z.string().catch(""), + dynamicSecretId: z.string().catch(""), connectionId: z.string().optional(), connectionName: z.string().optional() }); @@ -26,7 +27,8 @@ export const Route = createFileRoute( secretPath: "/", search: "", tags: "", - filterBy: "" + filterBy: "", + dynamicSecretId: "" }) ] }, From 15c341bce192d67d54f4b80ef0dd48a88cfe90e5 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Tue, 25 Nov 2025 20:42:29 -0300 Subject: [PATCH 10/24] refactor: restore native integrations tab and reorganize tab panels in IntegrationsListPage --- .../IntegrationsListPage.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx b/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx index 4b2d5f750b..77b4213b03 100644 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx +++ b/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx @@ -77,17 +77,17 @@ export const IntegrationsListPage = () => { Secret Syncs - {hasNativeIntegrations && ( - - Native Integrations - - )} Framework Integrations Infrastructure Integrations + {hasNativeIntegrations && ( + + Native Integrations + + )} { + + + + + + {hasNativeIntegrations && ( @@ -131,12 +137,6 @@ export const IntegrationsListPage = () => { )} - - - - - - From 28699023f5b48b50a6bfeb33e9a1cbf8aaa0c145 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Tue, 25 Nov 2025 15:47:09 -0800 Subject: [PATCH 11/24] Update dynamic-secret-lease-queue.ts --- .../dynamic-secret-lease-queue.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts index 4684ff2b14..224ad4c689 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts @@ -42,6 +42,8 @@ export type TDynamicSecretLeaseQueueServiceFactory = { init: () => Promise; }; +const MAX_REVOCATION_RETRY_COUNT = 10; + export const dynamicSecretLeaseQueueServiceFactory = ({ queueService, dynamicSecretDAL, @@ -96,7 +98,7 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ { singletonKey: `${leaseId}-retry`, // avoid conflicts with scheduled revocation retryDelay: retryDelaySeconds, - retryLimit: 10, // we dont want it to ever hit the limit, we want the expireInHours to take effect. + retryLimit: MAX_REVOCATION_RETRY_COUNT, // we dont want it to ever hit the limit, we want the expireInHours to take effect. expireInHours: 23 // if we set it to 24 hours, pgboss will complain that the expireIn is too high } ); @@ -111,7 +113,8 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ }, { jobId: `dynamic-secret-lease-revocation-failed-email-${dynamicSecretId}`, - delay: 1000 * 60, // 1 minute + delay: 1000 * 60 * 10, // 10 minute + attempts: 3, backoff: { type: "exponential", delay: 1000 * 60 // 1 minute @@ -234,8 +237,10 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, jobId); await queueService.stopRepeatableJobByJobId(QueueName.DynamicSecretRevocation, jobId); await queueFailedRevocation(leaseId, dynamicSecretId); + + // if its the last attempt, and the error isn't a DisableRotationErrors error, send an email to the project admins (debounced) } else if (isRetry && !(error instanceof DisableRotationErrors)) { - if (retryCount && retryCount === 10) { + if (retryCount && retryCount === MAX_REVOCATION_RETRY_COUNT) { await $queueDynamicSecretLeaseRevocationFailedEmail(leaseId, dynamicSecretId); } } @@ -252,6 +257,7 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ } }; + // send alert email once all revocation attempts have failed const $dynamicSecretLeaseRevocationFailedEmailJob = async (jobId: string, data: { leaseId: string }) => { try { const appCfg = getConfig(); From c9ce9ac306bb96d5b35c45b7188ae4a66186f034 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Tue, 25 Nov 2025 16:04:38 -0800 Subject: [PATCH 12/24] requested changes --- ...micSecretLeaseRevocationFailedTemplate.tsx | 2 +- .../platforms/infisical-agent.mdx | 20 +++++++++---------- .../platforms/kubernetes-injector.mdx | 6 +++--- .../DynamicSecretListView.tsx | 5 ++++- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/backend/src/services/smtp/emails/DynamicSecretLeaseRevocationFailedTemplate.tsx b/backend/src/services/smtp/emails/DynamicSecretLeaseRevocationFailedTemplate.tsx index 3cb982f646..94e2e8f6a7 100644 --- a/backend/src/services/smtp/emails/DynamicSecretLeaseRevocationFailedTemplate.tsx +++ b/backend/src/services/smtp/emails/DynamicSecretLeaseRevocationFailedTemplate.tsx @@ -37,7 +37,7 @@ export const DynamicSecretLeaseRevocationFailedTemplate = ({ multiple attempts. - Please review the dynamic secret lease and attempt to revoke it again. + Please review the dynamic secret leases and attempt to revoke them again. diff --git a/docs/integrations/platforms/infisical-agent.mdx b/docs/integrations/platforms/infisical-agent.mdx index 9f485edcc8..883d248ae7 100644 --- a/docs/integrations/platforms/infisical-agent.mdx +++ b/docs/integrations/platforms/infisical-agent.mdx @@ -10,7 +10,7 @@ It eliminates the need to modify application logic by enabling clients to decide ## Key Features -- **Token lifecycle maangement**: Automatically authenticates with Infisical and deposits renewed access tokens at specified path for applications to consume +- **Token lifecycle management**: Automatically authenticates with Infisical and deposits renewed access tokens at specified path for applications to consume - **Templating**: Renders secrets and dynamic secret leases via user provided templates to desired formats for applications to consume ## Token Renewal @@ -47,12 +47,12 @@ The secret template functions is what you will use to fetch resources such as st - + ```bash - listSecrets "" "environment-slug" "" "" + secret "" "environment-slug" "" "" ``` ```bash example-template-usage-1 - {{- with listSecrets "6553ccb2b7da580d7f6e7260" "dev" "/" `{"recursive": false, "expandSecretReferences": true}` }} + {{- with secret "6553ccb2b7da580d7f6e7260" "dev" "/" `{"recursive": false, "expandSecretReferences": true}` }} {{- range . }} {{ .Key }}={{ .Value }} {{- end }} @@ -72,7 +72,7 @@ The secret template functions is what you will use to fetch resources such as st - **Function name**: listSecrets + **Function name**: `secret` **Description**: This function can be used to render the full list of secrets within a given project, environment and secret path. @@ -96,7 +96,7 @@ The secret template functions is what you will use to fetch resources such as st {{ end }} ``` - **Function name**: getSecretByName + **Function name**: `getSecretByName` **Description**: This function can be used to render a single secret by it's name. @@ -116,7 +116,7 @@ The secret template functions is what you will use to fetch resources such as st ``` - **Function Name**: dynamic_secret + **Function Name**: `dynamic_secret` **Description**: This function can be used to render a dynamic secret lease credentials. The credentials are automatically renewed before they expire, ensuring that the rendered credentials are always up-to-date. @@ -135,13 +135,13 @@ The Infisical Agent supports clientside caching of Dynamic Secret leases. If the ### Persistent Caching -The Agent currently only supports persistent caching. To utilize persistent caching, you must be within a Kubernetes environment. We recommend using the [Infisical Agent Injector](/integrations/platforms/infisical-agent-injector) to inject the agent into pods within your Kubernetes cluster on demand. +The Agent currently only supports persistent caching. To utilize persistent caching, you must be within a Kubernetes environment. We recommend using the [Infisical Agent Injector](/integrations/platforms/kubernetes-injector) to inject the agent into pods within your Kubernetes cluster on demand. ### Cache eviction Cache eviction is the process of removing cached data from the cache. The Agent will automatically evict cached data when the cache is full during a garbage collection cycle which is triggered every 10 minutes. -The cache will automatically evict cached data that has gone stale or is about to go stale. For dynamic resources (such as dynamic secret leases), there's a TTL (Time-to-Live) associated with each lease which is used to determine if the lease is stale or about to go stale. +The cache will also automatically evict cached data that has gone stale or is about to go stale. For dynamic resources (such as dynamic secret leases), there's a TTL (Time-to-Live) associated with each lease which is used to determine if the lease is stale or about to go stale. If a stale dynamic secret lease is detected, it will be automatically evicted from the cache and replaced with a new up-to-date lease. @@ -159,7 +159,7 @@ Configuring the cache is done through the agent configuration file. The followin - Persistent caching is only supported within kubernetes environments at the moment. Please refer to the [Infisical Agent Injector](/integrations/platforms/infisical-agent-injector) documentation for more information on how to use persistent caching within Kubernetes environments. + Persistent caching is only supported within kubernetes environments at the moment. Please refer to the [Infisical Agent Injector](/integrations/platforms/kubernetes-injector) documentation for more information on how to use persistent caching within Kubernetes environments. ```yaml example-agent-config-file.yaml diff --git a/docs/integrations/platforms/kubernetes-injector.mdx b/docs/integrations/platforms/kubernetes-injector.mdx index 9e42b912ca..f51a96ab29 100644 --- a/docs/integrations/platforms/kubernetes-injector.mdx +++ b/docs/integrations/platforms/kubernetes-injector.mdx @@ -189,11 +189,11 @@ The Infisical Agent Injector supports the following annotations: - The maximum ephemeral storage limit for the agent containers. Doesn't have an explicit default vaule. The default value will conform to the default ephemeral storage limit for the pod. + The maximum ephemeral storage limit for the agent containers. Doesn't have an explicit default value. The default value will conform to the default ephemeral storage limit for the pod. - The minimum ephemeral storage request for the agent containers. Doesn't have an explicit default vaule. The default value will conform to the default ephemeral storage request for the pod. + The minimum ephemeral storage request for the agent containers. Doesn't have an explicit default value. The default value will conform to the default ephemeral storage request for the pod. @@ -280,7 +280,7 @@ The entire config needs to be of string format and needs to be assigned to the ` The path to the Kubernetes service account token to use for encrypting the persistent cache. Required when using `kubernetes` cache type. Defaults to `/var/run/secrets/kubernetes.io/serviceaccount/token`. - + It is recommended to use the annotation `org.infisical.com/agent-cache-enabled: "true"` instead of configuring the cache on the config map. Refer to the [Supported annotations](/integrations/platforms/kubernetes-injector#supported-annotations) documentation for more information on how to configure the cache through annotations. diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/DynamicSecretListView.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/DynamicSecretListView.tsx index ee19f81e19..ed6dc2824a 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/DynamicSecretListView.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/DynamicSecretListView.tsx @@ -76,7 +76,10 @@ export const DynamicSecretListView = ({ }; useEffect(() => { - if (selectedDynamicSecretId) { + if ( + selectedDynamicSecretId && + dynamicSecrets.find((secret) => secret.id === selectedDynamicSecretId) + ) { handlePopUpOpen("dynamicSecretLeases", selectedDynamicSecretId); } }, [selectedDynamicSecretId]); From e22369ec4d773eac3a6ceab806a8750d976bc431 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Wed, 26 Nov 2025 18:17:35 +0530 Subject: [PATCH 13/24] fix: review comments --- docs/cli/commands/login.mdx | 72 ++++++++++++++++++------------------- docs/cli/usage.mdx | 20 +++++------ 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/docs/cli/commands/login.mdx b/docs/cli/commands/login.mdx index 1a0db9b4e6..a670c03aae 100644 --- a/docs/cli/commands/login.mdx +++ b/docs/cli/commands/login.mdx @@ -43,34 +43,29 @@ User authentication is designed for individual developers and supports multiple The User authentication method allows you to log in with your email and password. This method supports three different login flows: -- **Browser Login** (default): Opens a browser for authentication -- **Direct Login**: Provide credentials via flags or environment variables for CI/CD -- **Interactive CLI Login**: Enter credentials via CLI prompts using `--interactive` + - **Browser Login** (default): Opens a browser for authentication + - **Direct Login**: Provide credentials via flags or environment variables for CI/CD + - **Interactive CLI Login**: Enter credentials via CLI prompts using `--interactive` -{" "} - - - - - Your email address. Required for direct login along with `--password` and - `--organization-id`. - - - Your password. Required for direct login along with `--email` and - `--organization-id`. - - - Your organization id. Required for direct login along with `--password` - and `--email`. - - - Force interactive CLI login instead of browser-based authentication. - - - Output only the JWT token (useful for scripting and CI/CD). - - - + + + + Your email address. Required for direct login along with `--password` and `--organization-id`. + + + Your password. Required for direct login along with `--email` and `--organization-id`. + + + Your organization id. Required for direct login along with `--password` and `--email`. + + + Force interactive CLI login instead of browser-based authentication. + + + Output only the JWT token (useful for scripting and CI/CD). + + + @@ -297,7 +292,6 @@ Machine identity authentication methods are designed for automated systems, serv ``` - @@ -323,7 +317,6 @@ Machine identity authentication methods are designed for automated systems, serv ``` - @@ -511,18 +504,21 @@ The login command supports a number of flags that you can use for different auth ```bash - infisical login --domain= [other-flags] + infisical login --domain= ``` #### Description - Specifies the Infisical API URL for non-US instances (EU Cloud or self-hosted instances). This flag is required when connecting to any instance other than the US Cloud. + Specifies the Infisical API URL for non-US Cloud instances. This flag is required when connecting to any instance other than US Cloud (e.g. EU Cloud or self-hosted). ```bash # Example for EU Cloud - infisical login --domain="https://eu.infisical.com" --jwt= --machine-identity-id= + infisical login --domain="https://eu.infisical.com" + + # Example for localhost + infisical login --domain="http://localhost:8080" # Example for self-hosted - infisical login --domain="https://your-self-hosted-infisical.com/api" --email user@example.com --password "password" + infisical login --domain="https://your-self-hosted-infisical.com" ``` @@ -558,11 +554,11 @@ The following examples demonstrate different ways to authenticate as a user with # Basic direct login (defaults to US Cloud) infisical login --email user@example.com --password "your-password" --organization-id "your-organization-id" - # EU Cloud + # Basic direct login (EU Cloud) infisical login --domain https://eu.infisical.com --email user@example.com --password "your-password" --organization-id "your-organization-id" - # Self-hosted instance - infisical login --domain https://your-self-hosted-infisical.com/api --email user@example.com --password "your-password" --organization-id "your-organization-id" + # Basic direct login (Self-hosted Instance) + infisical login --domain https://your-self-hosted-infisical.com --email user@example.com --password "your-password" --organization-id "your-organization-id" # Output only JWT token for scripting export INFISICAL_TOKEN=$(infisical login --email user@example.com --password "your-password" --organization-id "your-organization-id" --plain --silent) @@ -584,7 +580,7 @@ The following examples demonstrate different ways to authenticate as a user with ``` - **For non-US instances:** If you're using EU Cloud, or a self-hosted instance, you must set `INFISICAL_API_URL` before login, or use `--domain` on all commands. Refer to the [Domain Configuration](/cli/usage#domain-configuration) section for more details. + **For non-US Cloud instances:** If you're using EU Cloud or a self-hosted instance, you must set `INFISICAL_API_URL` before login or use `--domain` on all commands. Refer to the [Domain Configuration](/cli/usage#domain-configuration) section for more details. @@ -622,7 +618,7 @@ In this example we'll be using the `universal-auth` method to login to obtain an ``` - **For non-US instances:** If you're using EU Cloud, or a self-hosted instance, you must set `INFISICAL_API_URL` before login, or use `--domain` on all commands. Refer to the [Domain Configuration](/cli/usage#domain-configuration) section for more details. + **For non-US Cloud instances:** If you're using EU Cloud or a self-hosted instance, you must set `INFISICAL_API_URL` before login or use `--domain` on all commands. Refer to the [Domain Configuration](/cli/usage#domain-configuration) section for more details. Now that we've set the `INFISICAL_TOKEN` environment variable, we can use the CLI to interact with Infisical. The CLI will automatically check for the presence of the `INFISICAL_TOKEN` environment variable and use it for authentication. diff --git a/docs/cli/usage.mdx b/docs/cli/usage.mdx index c91ac57368..e158539db8 100644 --- a/docs/cli/usage.mdx +++ b/docs/cli/usage.mdx @@ -127,7 +127,7 @@ The CLI is designed for a variety of secret management applications ranging from Starting with CLI version v0.4.0, you can now choose to log in via Infisical Cloud (US/EU) or your own self-hosted instance by simply running `infisical login` and following the on-screen instructions — no need to manually set the `INFISICAL_API_URL` environment variable. -For versions prior to v0.4.0, the CLI defaults to the US Cloud. To connect to the EU Cloud or a self-hosted instance, set the `INFISICAL_API_URL` environment variable to `https://eu.infisical.com` or your custom URL. +For versions prior to v0.4.0, the CLI defaults to US Cloud. To connect to EU Cloud or a self-hosted instance, set the `INFISICAL_API_URL` environment variable to `https://eu.infisical.com` or your custom URL. @@ -136,7 +136,7 @@ For versions prior to v0.4.0, the CLI defaults to the US Cloud. To connect to th **Important:** If you're not using interactive login, you must configure the domain for **all CLI commands**. -The CLI defaults to the US Cloud (https://app.infisical.com). To connect to the **EU Cloud (https://eu.infisical.com)** or a **self-hosted instance**, you can configure the domain in one of the following ways: +The CLI defaults to US Cloud (https://app.infisical.com). To connect to **EU Cloud (https://eu.infisical.com)** or a **self-hosted instance**, you MUST configure the domain in one of the following ways: - Use the `INFISICAL_API_URL` environment variable - Use the `--domain` flag on every command @@ -175,7 +175,7 @@ The CLI defaults to the US Cloud (https://app.infisical.com). To connect to the infisical login --domain="https://your-domain.infisical.com" --method=universal-auth --client-id= --client-secret= --silent --plain # All subsequent commands must also include --domain - infisical secrets --domain="https://your-domain.infisical.com" --projectId --env dev + infisical secrets --domain="https://your-domain.infisical.com" --projectId= --env=dev ``` @@ -242,12 +242,12 @@ For security and privacy concerns, we recommend you to configure your terminal t ## FAQ - - Yes. The CLI is set to connect to Infisical US Cloud by default, but if you're using the EU Cloud, a self-hosted instance, you need to configure the domain for **all CLI commands**. + + Yes. The CLI is set to connect to Infisical US Cloud by default, but if you're using EU Cloud or a self-hosted instance you can to configure the domain for **all CLI commands**. - #### Method 1:Use the updated CLI (v0.4.0+) + #### Method 1: Use the updated CLI (v0.4.0+) - Beginning with CLI version V0.4.0, you can choose between logging in through the Infisical US Cloud, EU Cloud, or your own self-hosted instance. Simply execute the `infisical login` command and follow the on-screen instructions. + Beginning with CLI version V0.4.0, you can choose between logging in through Infisical US Cloud, EU Cloud, or your own self-hosted instance. Simply execute the `infisical login` command and follow the on-screen instructions. #### Method 2: Export environment variable @@ -287,15 +287,15 @@ For security and privacy concerns, we recommend you to configure your terminal t #### Method 3: Set manually on every command - If you prefer not to set the environment variable, you must include the `--domain` flag on **every CLI command** you run: + If you prefer not to use an environment variable, you must include the `--domain` flag on **every CLI command** you run: ```bash # Login with domain infisical login --domain="https://your-domain.infisical.com" --method=oidc-auth --jwt $JWT # All subsequent commands must also include --domain - infisical secrets --domain="https://your-self-hosted-infisical.com/api" --projectId --env dev - infisical export --domain="https://your-self-hosted-infisical.com/api" --format=dotenv-export + infisical secrets --domain="https://your-self-hosted-infisical.com" --projectId --env dev + infisical export --domain="https://your-self-hosted-infisical.com" --format=dotenv-export ``` From 40c80f4b6829568055ecaea0200613c9becc5732 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Wed, 26 Nov 2025 10:49:05 -0300 Subject: [PATCH 14/24] refactor: remove deprecated native integration error handling and streamline integration token saving process in API routes --- .../routes/v1/integration-auth-router.ts | 48 ++++------ .../server/routes/v1/integration-router.ts | 96 +++++++++---------- 2 files changed, 63 insertions(+), 81 deletions(-) diff --git a/backend/src/server/routes/v1/integration-auth-router.ts b/backend/src/server/routes/v1/integration-auth-router.ts index 40bb8fa13e..e5156724de 100644 --- a/backend/src/server/routes/v1/integration-auth-router.ts +++ b/backend/src/server/routes/v1/integration-auth-router.ts @@ -2,7 +2,6 @@ import { z } from "zod"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { ApiDocsTags, INTEGRATION_AUTH } from "@app/lib/api-docs"; -import { ForbiddenRequestError } from "@app/lib/errors"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -11,9 +10,6 @@ import { Integrations } from "@app/services/integration-auth/integration-list"; import { integrationAuthPubSchema } from "../sanitizedSchemas"; -const NATIVE_INTEGRATION_DEPRECATION_MESSAGE = - "We're moving Native Integrations to Secret Syncs. Check the documentation at https://infisical.com/docs/integrations/secret-syncs/overview. If the integration you need isn't available in the Secret Syncs, please get in touch with us at team@infisical.com."; - export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) => { server.route({ method: "GET", @@ -337,33 +333,27 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) }) } }, - handler: async (_) => { - throw new ForbiddenRequestError({ - message: NATIVE_INTEGRATION_DEPRECATION_MESSAGE + handler: async (req) => { + const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({ + actorId: req.permission.id, + actor: req.permission.type, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + projectId: req.body.workspaceId, + ...req.body }); - // We are keeping the old response commented out for an easy revert on the API if we need to before the full phase out. - - // const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({ - // actorId: req.permission.id, - // actor: req.permission.type, - // actorAuthMethod: req.permission.authMethod, - // actorOrgId: req.permission.orgId, - // projectId: req.body.workspaceId, - // ...req.body - // }); - - // await server.services.auditLog.createAuditLog({ - // ...req.auditLogInfo, - // projectId: req.body.workspaceId, - // event: { - // type: EventType.AUTHORIZE_INTEGRATION, - // metadata: { - // integration: integrationAuth.integration - // } - // } - // }); - // return { integrationAuth }; + await server.services.auditLog.createAuditLog({ + ...req.auditLogInfo, + projectId: req.body.workspaceId, + event: { + type: EventType.AUTHORIZE_INTEGRATION, + metadata: { + integration: integrationAuth.integration + } + } + }); + return { integrationAuth }; } }); diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 183c5a38ba..95477c3419 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -3,19 +3,17 @@ import { z } from "zod"; import { IntegrationsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { ApiDocsTags, INTEGRATION } from "@app/lib/api-docs"; -import { ForbiddenRequestError } from "@app/lib/errors"; import { removeTrailingSlash, shake } from "@app/lib/fn"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; +import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; import { IntegrationMetadataSchema } from "@app/services/integration/integration-schema"; import { Integrations } from "@app/services/integration-auth/integration-list"; +import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telemetry/telemetry-types"; import {} from "../sanitizedSchemas"; -const NATIVE_INTEGRATION_DEPRECATION_MESSAGE = - "We're moving Native Integrations to Secret Syncs. Check the documentation at https://infisical.com/docs/integrations/secret-syncs/overview. If the integration you need isn't available in the Secret Syncs, please get in touch with us at team@infisical.com."; - export const registerIntegrationRouter = async (server: FastifyZodProvider) => { server.route({ method: "POST", @@ -68,58 +66,52 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { } }, onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), - handler: async (_) => { - throw new ForbiddenRequestError({ - message: NATIVE_INTEGRATION_DEPRECATION_MESSAGE + handler: async (req) => { + const { integration, integrationAuth } = await server.services.integration.createIntegration({ + actorId: req.permission.id, + actor: req.permission.type, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + ...req.body }); - // We are keeping the old response commented out for an easy revert on the API if we need to before the full phase out. + const createIntegrationEventProperty = shake({ + integrationId: integration.id.toString(), + integration: integration.integration, + environment: req.body.sourceEnvironment, + secretPath: req.body.secretPath, + url: integration.url, + app: integration.app, + appId: integration.appId, + targetEnvironment: integration.targetEnvironment, + targetEnvironmentId: integration.targetEnvironmentId, + targetService: integration.targetService, + targetServiceId: integration.targetServiceId, + path: integration.path, + region: integration.region + }) as TIntegrationCreatedEvent["properties"]; - // const { integration, integrationAuth } = await server.services.integration.createIntegration({ - // actorId: req.permission.id, - // actor: req.permission.type, - // actorAuthMethod: req.permission.authMethod, - // actorOrgId: req.permission.orgId, - // ...req.body - // }); + await server.services.auditLog.createAuditLog({ + ...req.auditLogInfo, + projectId: integrationAuth.projectId, + event: { + type: EventType.CREATE_INTEGRATION, + // eslint-disable-next-line + metadata: createIntegrationEventProperty + } + }); - // const createIntegrationEventProperty = shake({ - // integrationId: integration.id.toString(), - // integration: integration.integration, - // environment: req.body.sourceEnvironment, - // secretPath: req.body.secretPath, - // url: integration.url, - // app: integration.app, - // appId: integration.appId, - // targetEnvironment: integration.targetEnvironment, - // targetEnvironmentId: integration.targetEnvironmentId, - // targetService: integration.targetService, - // targetServiceId: integration.targetServiceId, - // path: integration.path, - // region: integration.region - // }) as TIntegrationCreatedEvent["properties"]; - - // await server.services.auditLog.createAuditLog({ - // ...req.auditLogInfo, - // projectId: integrationAuth.projectId, - // event: { - // type: EventType.CREATE_INTEGRATION, - // // eslint-disable-next-line - // metadata: createIntegrationEventProperty - // } - // }); - - // await server.services.telemetry.sendPostHogEvents({ - // event: PostHogEventTypes.IntegrationCreated, - // organizationId: req.permission.orgId, - // distinctId: getTelemetryDistinctId(req), - // properties: { - // ...createIntegrationEventProperty, - // projectId: integrationAuth.projectId, - // ...req.auditLogInfo - // } - // }); - // return { integration }; + await server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.IntegrationCreated, + organizationId: req.permission.orgId, + distinctId: getTelemetryDistinctId(req), + properties: { + ...createIntegrationEventProperty, + projectId: integrationAuth.projectId, + ...req.auditLogInfo + } + }); + return { integration }; } }); From 0e49aaf1e605f5764a872b0a24bfdcc16faab96c Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Wed, 26 Nov 2025 11:44:54 -0300 Subject: [PATCH 15/24] feat: add telemetry events for integration deletion and synchronization, including new event types and properties --- .../server/routes/v1/integration-router.ts | 99 ++++++++++++------- .../src/services/telemetry/telemetry-types.ts | 45 +++++++++ 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 95477c3419..5670edf777 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -10,7 +10,12 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; import { IntegrationMetadataSchema } from "@app/services/integration/integration-schema"; import { Integrations } from "@app/services/integration-auth/integration-list"; -import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telemetry/telemetry-types"; +import { + PostHogEventTypes, + TIntegrationCreatedEvent, + TIntegrationDeletedEvent, + TIntegrationSyncedEvent +} from "@app/services/telemetry/telemetry-types"; import {} from "../sanitizedSchemas"; @@ -288,31 +293,46 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { shouldDeleteIntegrationSecrets: req.query.shouldDeleteIntegrationSecrets }); + const deleteIntegrationEventProperty = shake({ + integrationId: integration.id, + integration: integration.integration, + environment: integration.environment.slug, + secretPath: integration.secretPath, + url: integration.url, + app: integration.app, + appId: integration.appId, + targetEnvironment: integration.targetEnvironment, + targetEnvironmentId: integration.targetEnvironmentId, + targetService: integration.targetService, + targetServiceId: integration.targetServiceId, + path: integration.path, + region: integration.region + }) as TIntegrationDeletedEvent["properties"]; + await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, projectId: integration.projectId, event: { type: EventType.DELETE_INTEGRATION, // eslint-disable-next-line - metadata: shake({ - integrationId: integration.id, - integration: integration.integration, - environment: integration.environment.slug, - secretPath: integration.secretPath, - url: integration.url, - app: integration.app, - appId: integration.appId, - targetEnvironment: integration.targetEnvironment, - targetEnvironmentId: integration.targetEnvironmentId, - targetService: integration.targetService, - targetServiceId: integration.targetServiceId, - path: integration.path, - region: integration.region, + metadata: { + ...deleteIntegrationEventProperty, shouldDeleteIntegrationSecrets: req.query.shouldDeleteIntegrationSecrets - // eslint-disable-next-line - }) as any + } as any } }); + + await server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.IntegrationDeleted, + organizationId: req.permission.orgId, + distinctId: getTelemetryDistinctId(req), + properties: { + ...deleteIntegrationEventProperty, + projectId: integration.projectId, + ...req.auditLogInfo + } + }); + return { integration }; } }); @@ -351,28 +371,41 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { id: req.params.integrationId }); + const syncIntegrationEventProperty = shake({ + integrationId: integration.id, + integration: integration.integration, + environment: integration.environment.slug, + secretPath: integration.secretPath, + url: integration.url, + app: integration.app, + appId: integration.appId, + targetEnvironment: integration.targetEnvironment, + targetEnvironmentId: integration.targetEnvironmentId, + targetService: integration.targetService, + targetServiceId: integration.targetServiceId, + path: integration.path, + region: integration.region + }) as TIntegrationSyncedEvent["properties"]; + await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, projectId: integration.projectId, event: { type: EventType.MANUAL_SYNC_INTEGRATION, // eslint-disable-next-line - metadata: shake({ - integrationId: integration.id, - integration: integration.integration, - environment: integration.environment.slug, - secretPath: integration.secretPath, - url: integration.url, - app: integration.app, - appId: integration.appId, - targetEnvironment: integration.targetEnvironment, - targetEnvironmentId: integration.targetEnvironmentId, - targetService: integration.targetService, - targetServiceId: integration.targetServiceId, - path: integration.path, - region: integration.region - // eslint-disable-next-line - }) as any + metadata: syncIntegrationEventProperty as any + } + }); + + await server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.IntegrationSynced, + organizationId: req.permission.orgId, + distinctId: getTelemetryDistinctId(req), + properties: { + ...syncIntegrationEventProperty, + projectId: integration.projectId, + isManualSync: true, + ...req.auditLogInfo } }); diff --git a/backend/src/services/telemetry/telemetry-types.ts b/backend/src/services/telemetry/telemetry-types.ts index de466614ad..d2e977605b 100644 --- a/backend/src/services/telemetry/telemetry-types.ts +++ b/backend/src/services/telemetry/telemetry-types.ts @@ -21,6 +21,8 @@ export enum PostHogEventTypes { SecretScannerPush = "cloud secret scan", ProjectCreated = "Project Created", IntegrationCreated = "Integration Created", + IntegrationSynced = "Integration Synced", + IntegrationDeleted = "Integration Deleted", MachineIdentityCreated = "Machine Identity Created", UserOrgInvitation = "User Org Invitation", TelemetryInstanceStats = "Self Hosted Instance Stats", @@ -126,6 +128,47 @@ export type TIntegrationCreatedEvent = { }; }; +export type TIntegrationSyncedEvent = { + event: PostHogEventTypes.IntegrationSynced; + properties: { + projectId: string; + integrationId: string; + integration: string; + environment: string; + secretPath: string; + isManualSync: boolean; + url?: string; + app?: string; + appId?: string; + targetEnvironment?: string; + targetEnvironmentId?: string; + targetService?: string; + targetServiceId?: string; + path?: string; + region?: string; + }; +}; + +export type TIntegrationDeletedEvent = { + event: PostHogEventTypes.IntegrationDeleted; + properties: { + projectId: string; + integrationId: string; + integration: string; + environment: string; + secretPath: string; + url?: string; + app?: string; + appId?: string; + targetEnvironment?: string; + targetEnvironmentId?: string; + targetService?: string; + targetServiceId?: string; + path?: string; + region?: string; + }; +}; + export type TUserOrgInvitedEvent = { event: PostHogEventTypes.UserOrgInvitation; properties: { @@ -249,6 +292,8 @@ export type TPostHogEvent = { distinctId: string; organizationId?: string } & ( | TUserOrgInvitedEvent | TMachineIdentityCreatedEvent | TIntegrationCreatedEvent + | TIntegrationSyncedEvent + | TIntegrationDeletedEvent | TProjectCreateEvent | TTelemetryInstanceStatsEvent | TSecretRequestCreatedEvent From ffe866efb05637fbd3524f7abfb5603b47d9de06 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Wed, 26 Nov 2025 12:02:23 -0300 Subject: [PATCH 16/24] fix: add missing eslint-disable comments for metadata in integration deletion event --- backend/src/server/routes/v1/integration-router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 5670edf777..1d3a6a8b0c 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -314,10 +314,10 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { projectId: integration.projectId, event: { type: EventType.DELETE_INTEGRATION, - // eslint-disable-next-line metadata: { ...deleteIntegrationEventProperty, shouldDeleteIntegrationSecrets: req.query.shouldDeleteIntegrationSecrets + // eslint-disable-next-line } as any } }); From f98b803e76c0dca6263838cec3d0c3bbc1e0ab21 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Wed, 26 Nov 2025 12:05:08 -0300 Subject: [PATCH 17/24] fix: add eslint-disable comment for shouldDeleteIntegrationSecrets in integration deletion event metadata --- backend/src/server/routes/v1/integration-router.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 1d3a6a8b0c..7a3b84c5a4 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -314,6 +314,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { projectId: integration.projectId, event: { type: EventType.DELETE_INTEGRATION, + // eslint-disable-next-line metadata: { ...deleteIntegrationEventProperty, shouldDeleteIntegrationSecrets: req.query.shouldDeleteIntegrationSecrets From b97691dd9fd82c6e9b741ffb37f816fcb6d28b59 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Wed, 26 Nov 2025 22:47:01 +0530 Subject: [PATCH 18/24] fix: review comments --- docs/integrations/platforms/aws/lambda.mdx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/integrations/platforms/aws/lambda.mdx b/docs/integrations/platforms/aws/lambda.mdx index 496527651c..8376e98c24 100644 --- a/docs/integrations/platforms/aws/lambda.mdx +++ b/docs/integrations/platforms/aws/lambda.mdx @@ -4,8 +4,7 @@ sidebarTitle: "AWS Lambda" description: "How to use Infisical secrets in AWS Lambda" --- -Learn how to sync Infisical secrets to AWS Lambda regardless of how you deploy your function. -This guide covers the following strategies: +Learn how to sync Infisical secrets to AWS Lambda regardless of how you deploy your function. This guide covers the following strategies: - Infisical SDKs - AWS Secrets Manager integration @@ -86,10 +85,10 @@ On success, the updated `Environment.Variables` block will be returned. Verify the values in the Lambda console or by invoking the function. - Automate this step in CI/CD. Run `infisical export` using an Infisical API - Token scoped to your project and environment, and trigger the sync as part of - your deployment workflow. Learn more about the [Infisical API - Token](/cli/commands/login#user:plain-token-output-useful-for-scripting-and-ci-cd). + Automate this step in CI/CD. Run `infisical export` using an Infisical Token + scoped to your project and environment, and trigger the sync as part of your + deployment workflow. Learn more about the [Infisical + Token](/cli/commands/export#infisical-export:infisical-token). From 41c1ab175f0b00d067bae8fb340abd2013e83946 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 26 Nov 2025 11:45:15 -0800 Subject: [PATCH 19/24] Update dynamic-secret-lease-queue.ts --- .../dynamic-secret-lease-queue.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts index 224ad4c689..d62a1eeb2b 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue.ts @@ -105,6 +105,10 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ }; const $queueDynamicSecretLeaseRevocationFailedEmail = async (leaseId: string, dynamicSecretId: string) => { + const appConfig = getConfig(); + + const delay = appConfig.isDevelopmentMode ? 1_000 * 60 : 1_000 * 60 * 15; // 1 minute in development, 15 minutes in production + await queueService.queue( QueueName.DynamicSecretLeaseRevocationFailedEmail, QueueJobs.DynamicSecretLeaseRevocationFailedEmail, @@ -113,7 +117,7 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ }, { jobId: `dynamic-secret-lease-revocation-failed-email-${dynamicSecretId}`, - delay: 1000 * 60 * 10, // 10 minute + delay, attempts: 3, backoff: { type: "exponential", @@ -241,6 +245,13 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ // if its the last attempt, and the error isn't a DisableRotationErrors error, send an email to the project admins (debounced) } else if (isRetry && !(error instanceof DisableRotationErrors)) { if (retryCount && retryCount === MAX_REVOCATION_RETRY_COUNT) { + // if all retries fail, we should also stop the automatic revocation job. + // the ID of the revocation job is set to the leaseId, so we can use that to stop the job + + // we dont have to stop the retry job, because if we hit this point, its the last attempt and the retry job will be stopped by pgboss itself after this point, + await queueService.stopJobByIdPg(QueueName.DynamicSecretRevocation, leaseId); + await queueService.stopRepeatableJobByJobId(QueueName.DynamicSecretRevocation, leaseId); + await $queueDynamicSecretLeaseRevocationFailedEmail(leaseId, dynamicSecretId); } } @@ -273,11 +284,6 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ throw new DisableRotationErrors({ message: "Dynamic secret lease not found" }); } - const dynamicSecret = await dynamicSecretDAL.findOne({ id: lease.dynamicSecretId }); - if (!dynamicSecret) { - throw new DisableRotationErrors({ message: "Dynamic secret not found" }); - } - const folder = await folderDAL.findById(lease.dynamicSecret.folderId); if (!folder) throw new NotFoundError({ message: `Failed to find folder with ${lease.dynamicSecret.folderId}` }); @@ -293,7 +299,7 @@ export const dynamicSecretLeaseQueueServiceFactory = ({ template: SmtpTemplates.DynamicSecretLeaseRevocationFailed, subjectLine: "Dynamic Secret Lease Revocation Failed", substitutions: { - dynamicSecretLeaseUrl: `${appCfg.SITE_URL}/organizations/${project.orgId}/projects/secret-management/${project.id}/secrets/${folder.environment.envSlug}?dynamicSecretId=${lease.dynamicSecret.id}&filterBy=dynamic&search=${dynamicSecret.name}`, + dynamicSecretLeaseUrl: `${appCfg.SITE_URL}/organizations/${project.orgId}/projects/secret-management/${project.id}/secrets/${folder.environment.envSlug}?dynamicSecretId=${lease.dynamicSecret.id}&filterBy=dynamic&search=${lease.dynamicSecret.name}`, dynamicSecretName: lease.dynamicSecret.name, projectName: project.name, environmentSlug: folder.environment.envSlug, From 9ffb4406391db7df0324b7cca047a5a4b4506ae4 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Wed, 26 Nov 2025 16:52:12 -0300 Subject: [PATCH 20/24] feat: integrate telemetry service for secret synchronization events and update project role permissions to reflect workspace integrations --- backend/src/server/routes/index.ts | 3 +- backend/src/services/secret/secret-queue.ts | 29 ++++++++- docs/docs.json | 1 - docs/integrations/cicd/checkly.mdx | 45 -------------- .../components/PolicySelectionModal.tsx | 13 +++- .../components/RolePermissionsSection.tsx | 13 +++- .../IntegrationsListPage.tsx | 61 +++++++++---------- 7 files changed, 84 insertions(+), 81 deletions(-) delete mode 100644 docs/integrations/cicd/checkly.mdx diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 00771168cf..0d79999578 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -1329,7 +1329,8 @@ export const registerRoutes = async ( eventBusService, licenseService, membershipRoleDAL, - membershipUserDAL + membershipUserDAL, + telemetryService }); const projectService = projectServiceFactory({ diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index eefa0764f3..5246aa8d1f 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -64,6 +64,8 @@ import { expandSecretReferencesFactory, getAllSecretReferences } from "../secret import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal"; import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal"; import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; +import { TTelemetryServiceFactory } from "../telemetry/telemetry-service"; +import { PostHogEventTypes } from "../telemetry/telemetry-types"; import { TUserDALFactory } from "../user/user-dal"; import { TWebhookDALFactory } from "../webhook/webhook-dal"; import { fnTriggerWebhook } from "../webhook/webhook-fns"; @@ -120,6 +122,7 @@ type TSecretQueueFactoryDep = { reminderService: Pick; eventBusService: TEventBusService; licenseService: Pick; + telemetryService: Pick; }; export type TGetSecrets = { @@ -184,7 +187,8 @@ export const secretQueueFactory = ({ eventBusService, licenseService, membershipUserDAL, - membershipRoleDAL + membershipRoleDAL, + telemetryService }: TSecretQueueFactoryDep) => { const integrationMeter = opentelemetry.metrics.getMeter("Integrations"); const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", { @@ -1029,6 +1033,29 @@ export const secretQueueFactory = ({ isSynced: response?.isSynced ?? true }); + await telemetryService.sendPostHogEvents({ + event: PostHogEventTypes.IntegrationSynced, + distinctId: `project/${projectId}`, + organizationId: project.orgId, + properties: { + integrationId: integration.id, + integration: integration.integration, + environment, + secretPath, + projectId, + url: integration.url ?? undefined, + app: integration.app ?? undefined, + appId: integration.appId ?? undefined, + targetEnvironment: integration.targetEnvironment ?? undefined, + targetEnvironmentId: integration.targetEnvironmentId ?? undefined, + targetService: integration.targetService ?? undefined, + targetServiceId: integration.targetServiceId ?? undefined, + path: integration.path ?? undefined, + region: integration.region ?? undefined, + isManualSync: isManual ?? false + } + }); + // May be undefined, if it's undefined we assume the sync was successful, hence the strict equality type check. if (response?.isSynced === false) { integrationsFailedToSync.push({ diff --git a/docs/docs.json b/docs/docs.json index 27d93b2c86..68ac594fbd 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -574,7 +574,6 @@ "pages": [ "integrations/cicd/aws-amplify", "integrations/cicd/bitbucket", - "integrations/cicd/checkly", "integrations/cicd/githubactions", "integrations/cicd/gitlab", "integrations/cicd/jenkins" diff --git a/docs/integrations/cicd/checkly.mdx b/docs/integrations/cicd/checkly.mdx deleted file mode 100644 index 00ec38d2f1..0000000000 --- a/docs/integrations/cicd/checkly.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: "Checkly" -description: "How to sync secrets from Infisical to Checkly" ---- - -Prerequisites: - -- Set up and add envars to [Infisical Cloud](https://app.infisical.com) - - - - Obtain a Checkly API Key in User Settings > API Keys. - - ![integrations checkly dashboard](../../images/integrations/checkly/integrations-checkly-dashboard.png) - ![integrations checkly token](../../images/integrations/checkly/integrations-checkly-token.png) - - Navigate to your project's integrations tab in Infisical. - - ![integrations](../../images/integrations.png) - - Press on the Checkly tile and input your Checkly API Key to grant Infisical access to your Checkly account. - - ![integrations checkly authorization](../../images/integrations/checkly/integrations-checkly-auth.png) - - - - Select which Infisical environment secrets you want to sync to Checkly and press create integration to start syncing secrets. - - ![integrations checkly](../../images/integrations/checkly/integrations-checkly-create.png) - - - Infisical integrates with Checkly's environment variables at the **global** and **group** levels. - - To sync secrets to a specific group, you can select a group from the Checkly Group dropdown; otherwise, leaving it empty will sync secrets globally. - - - ![integrations checkly](../../images/integrations/checkly/integrations-checkly.png) - - - In the new version of the Checkly integration, you are able to specify suffixes that depend on the secrets' environment and path. - If you choose to do so, you should utilize such suffixes for ALL Checkly integrations – otherwise the integration system - might run into issues with deleting secrets from the wrong environments. - - - \ No newline at end of file diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal.tsx index 63853ad511..3505722bc1 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal.tsx @@ -18,7 +18,8 @@ import { Tooltip, Tr } from "@app/components/v2"; -import { ProjectPermissionSub } from "@app/context"; +import { ProjectPermissionSub, useProject } from "@app/context"; +import { useGetWorkspaceIntegrations } from "@app/hooks/api"; import { ProjectType } from "@app/hooks/api/projects/types"; import { @@ -46,6 +47,9 @@ type TForm = { permissions: Record }; const Content = ({ onClose, type: projectType }: ContentProps) => { const rootForm = useFormContext(); const [search, setSearch] = useState(""); + const { currentProject } = useProject(); + const { data: integrations = [] } = useGetWorkspaceIntegrations(currentProject?.id ?? ""); + const { control, handleSubmit, @@ -60,6 +64,8 @@ const Content = ({ onClose, type: projectType }: ContentProps) => { } }); + const hasNativeIntegrations = integrations.length > 0; + const filteredPolicies = Object.entries(PROJECT_PERMISSION_OBJECT) .filter( ([subject, { title }]) => @@ -68,6 +74,11 @@ const Content = ({ onClose, type: projectType }: ContentProps) => { ] && (search ? title.toLowerCase().includes(search.toLowerCase()) : true) ) .filter(([subject]) => !EXCLUDED_PERMISSION_SUBS.includes(subject as ProjectPermissionSub)) + .filter( + ([subject]) => + // Hide Native Integrations policy if project has no integrations + subject !== ProjectPermissionSub.Integrations || hasNativeIntegrations + ) .sort((a, b) => a[1].title.localeCompare(b[1].title)) .map(([subject]) => subject); diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/RolePermissionsSection.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/RolePermissionsSection.tsx index c93cea5346..f50b39a920 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/RolePermissionsSection.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/RolePermissionsSection.tsx @@ -11,7 +11,11 @@ import { Button } from "@app/components/v2"; import { ProjectPermissionSub, useProject } from "@app/context"; import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext"; import { evaluatePermissionsAbility } from "@app/helpers/permissions"; -import { useGetProjectRoleBySlug, useUpdateProjectRole } from "@app/hooks/api"; +import { + useGetProjectRoleBySlug, + useGetWorkspaceIntegrations, + useUpdateProjectRole +} from "@app/hooks/api"; import { ProjectType } from "@app/hooks/api/projects/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; @@ -105,6 +109,8 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => { currentProject?.id ?? "", roleSlug as string ); + const { data: integrations = [] } = useGetWorkspaceIntegrations(projectId); + const hasNativeIntegrations = integrations.length > 0; const [showAccessTree, setShowAccessTree] = useState(null); @@ -198,6 +204,11 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => { {!isPending && } {(Object.keys(PROJECT_PERMISSION_OBJECT) as ProjectPermissionSub[]) .filter((subject) => !EXCLUDED_PERMISSION_SUBS.includes(subject)) + .filter( + (subject) => + // Hide Native Integrations policy if project has no integrations + subject !== ProjectPermissionSub.Integrations || hasNativeIntegrations + ) .map((subject) => ( { {hasNativeIntegrations && ( - - - We're moving Native Integrations to{" "} - - Secret Syncs - - . If the integration you need isn't available in the Secret Syncs menu, - please get in touch with us at{" "} - - team@infisical.com - - . - - +
+
+ +
+

+ We're moving Native Integrations to{" "} + + Secret Syncs + + . If the integration you need isn't available in the Secret Syncs menu, + please get in touch with us at{" "} + + team@infisical.com + + . +

+
+
+
Date: Wed, 26 Nov 2025 14:45:53 -0800 Subject: [PATCH 21/24] style: minor banner adjustments --- .../IntegrationsListPage/IntegrationsListPage.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx b/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx index 4d371c2993..9d5774d5dd 100644 --- a/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx +++ b/frontend/src/pages/secret-manager/IntegrationsListPage/IntegrationsListPage.tsx @@ -100,11 +100,11 @@ export const IntegrationsListPage = () => {
{hasNativeIntegrations && ( -
+
-

+

We're moving Native Integrations to{" "} { > Secret Syncs - . If the integration you need isn't available in the Secret Syncs menu, + . +

+

+ If the integration you need isn't available in the Secret Syncs menu, please get in touch with us at{" "} Date: Wed, 26 Nov 2025 20:28:55 -0300 Subject: [PATCH 22/24] fix(identity-oidc-auth): handle errors when fetching OIDC discovery document and validate jwks_uri --- .../identity-oidc-auth-service.ts | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts b/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts index a253c1e95b..a5178f36d3 100644 --- a/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts +++ b/backend/src/services/identity-oidc-auth/identity-oidc-auth-service.ts @@ -99,13 +99,28 @@ export const identityOidcAuthServiceFactory = ({ } const requestAgent = new https.Agent({ ca: caCert, rejectUnauthorized: !!caCert }); - const { data: discoveryDoc } = await axios.get<{ jwks_uri: string }>( - `${identityOidcAuth.oidcDiscoveryUrl}/.well-known/openid-configuration`, - { - httpsAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined - } - ); + + let discoveryDoc: { jwks_uri: string }; + try { + const response = await axios.get<{ jwks_uri: string }>( + `${identityOidcAuth.oidcDiscoveryUrl}/.well-known/openid-configuration`, + { + httpsAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined + } + ); + discoveryDoc = response.data; + } catch (error) { + throw new UnauthorizedError({ + message: `Access denied: Failed to fetch OIDC discovery document from ${identityOidcAuth.oidcDiscoveryUrl}. ${error instanceof Error ? error.message : String(error)}` + }); + } + const jwksUri = discoveryDoc.jwks_uri; + if (!jwksUri) { + throw new UnauthorizedError({ + message: `Access denied: OIDC discovery document does not contain a jwks_uri. The identity provider may be misconfigured.` + }); + } const decodedToken = crypto.jwt().decode(oidcJwt, { complete: true }); if (!decodedToken) { From dbf9d8c57d11ab682abab285ffc9fa3367b03f36 Mon Sep 17 00:00:00 2001 From: x032205 Date: Wed, 26 Nov 2025 19:23:16 -0500 Subject: [PATCH 23/24] make MUST lowercase --- docs/cli/usage.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli/usage.mdx b/docs/cli/usage.mdx index e158539db8..16c35b3b6c 100644 --- a/docs/cli/usage.mdx +++ b/docs/cli/usage.mdx @@ -136,7 +136,7 @@ For versions prior to v0.4.0, the CLI defaults to US Cloud. To connect to EU Clo **Important:** If you're not using interactive login, you must configure the domain for **all CLI commands**. -The CLI defaults to US Cloud (https://app.infisical.com). To connect to **EU Cloud (https://eu.infisical.com)** or a **self-hosted instance**, you MUST configure the domain in one of the following ways: +The CLI defaults to US Cloud (https://app.infisical.com). To connect to **EU Cloud (https://eu.infisical.com)** or a **self-hosted instance**, you must configure the domain in one of the following ways: - Use the `INFISICAL_API_URL` environment variable - Use the `--domain` flag on every command From af2c1b31bce15c922d63c4053ae1b373af43f308 Mon Sep 17 00:00:00 2001 From: x032205 Date: Wed, 26 Nov 2025 19:25:25 -0500 Subject: [PATCH 24/24] remove unnecessary "to" --- docs/cli/usage.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli/usage.mdx b/docs/cli/usage.mdx index 16c35b3b6c..04a7cb025b 100644 --- a/docs/cli/usage.mdx +++ b/docs/cli/usage.mdx @@ -243,7 +243,7 @@ For security and privacy concerns, we recommend you to configure your terminal t - Yes. The CLI is set to connect to Infisical US Cloud by default, but if you're using EU Cloud or a self-hosted instance you can to configure the domain for **all CLI commands**. + Yes. The CLI is set to connect to Infisical US Cloud by default, but if you're using EU Cloud or a self-hosted instance you can configure the domain for **all CLI commands**. #### Method 1: Use the updated CLI (v0.4.0+)