diff --git a/backend/src/ee/services/external-kms/external-kms-service.ts b/backend/src/ee/services/external-kms/external-kms-service.ts index 8dd80ce2c8..faaace343c 100644 --- a/backend/src/ee/services/external-kms/external-kms-service.ts +++ b/backend/src/ee/services/external-kms/external-kms-service.ts @@ -1,7 +1,9 @@ +import { KMSServiceException } from "@aws-sdk/client-kms"; +import { STSServiceException } from "@aws-sdk/client-sts"; import { ForbiddenError } from "@casl/ability"; import slugify from "@sindresorhus/slugify"; -import { BadRequestError, NotFoundError } from "@app/lib/errors"; +import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; @@ -71,7 +73,16 @@ export const externalKmsServiceFactory = ({ switch (provider.type) { case KmsProviders.Aws: { - const externalKms = await AwsKmsProviderFactory({ inputs: provider.inputs }); + const externalKms = await AwsKmsProviderFactory({ inputs: provider.inputs }).catch((error) => { + if (error instanceof STSServiceException || error instanceof KMSServiceException) { + throw new InternalServerError({ + message: error.message ? `AWS error: ${error.message}` : "" + }); + } + + throw error; + }); + // if missing kms key this generate a new kms key id and returns new provider input const newProviderInput = await externalKms.generateInputKmsKey(); sanitizedProviderInput = JSON.stringify(newProviderInput); diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 599fa1f960..762ca298de 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -199,7 +199,29 @@ const envSchema = z INF_APP_CONNECTION_GITHUB_APP_CLIENT_SECRET: zpStr(z.string().optional()), INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY: zpStr(z.string().optional()), INF_APP_CONNECTION_GITHUB_APP_SLUG: zpStr(z.string().optional()), - INF_APP_CONNECTION_GITHUB_APP_ID: zpStr(z.string().optional()) + INF_APP_CONNECTION_GITHUB_APP_ID: zpStr(z.string().optional()), + + /* CORS ----------------------------------------------------------------------------- */ + + CORS_ALLOWED_ORIGINS: zpStr( + z + .string() + .optional() + .transform((val) => { + if (!val) return undefined; + return JSON.parse(val) as string[]; + }) + ), + + CORS_ALLOWED_HEADERS: zpStr( + z + .string() + .optional() + .transform((val) => { + if (!val) return undefined; + return JSON.parse(val) as string[]; + }) + ) }) // To ensure that basic encryption is always possible. .refine( diff --git a/backend/src/server/app.ts b/backend/src/server/app.ts index d001d900e7..ce1be4a04e 100644 --- a/backend/src/server/app.ts +++ b/backend/src/server/app.ts @@ -87,7 +87,16 @@ export const main = async ({ db, hsmModule, auditLogDb, smtp, logger, queue, key await server.register(cors, { credentials: true, - origin: appCfg.SITE_URL || true + ...(appCfg.CORS_ALLOWED_ORIGINS?.length + ? { + origin: [...appCfg.CORS_ALLOWED_ORIGINS, ...(appCfg.SITE_URL ? [appCfg.SITE_URL] : [])] + } + : { + origin: appCfg.SITE_URL || true + }), + ...(appCfg.CORS_ALLOWED_HEADERS?.length && { + allowedHeaders: appCfg.CORS_ALLOWED_HEADERS + }) }); await server.register(addErrorsToResponseSchemas); diff --git a/backend/src/services/integration-auth/integration-sync-secret.ts b/backend/src/services/integration-auth/integration-sync-secret.ts index 8d9ca0d3ac..faaec770c9 100644 --- a/backend/src/services/integration-auth/integration-sync-secret.ts +++ b/backend/src/services/integration-auth/integration-sync-secret.ts @@ -3714,7 +3714,8 @@ const syncSecretsCloudflarePages = async ({ }>(`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`, { headers: { Authorization: `Bearer ${accessToken}`, - Accept: "application/json" + Accept: "application/json", + "Cache-Control": "no-cache" } }) ).data.result.deployment_configs[integration.targetEnvironment as string].env_vars; diff --git a/company/documentation/engineering/oncall-summery-template.mdx b/company/documentation/engineering/oncall-summery-template.mdx new file mode 100644 index 0000000000..523f71e008 --- /dev/null +++ b/company/documentation/engineering/oncall-summery-template.mdx @@ -0,0 +1,22 @@ +--- +title: "On call summary template" +sidebarTitle: "Summary template" +--- + +```plain +Date: MM/DD/YY-MM/DD/YY + +Notable incidents: +- []
+ - Action items: + - + +Notable support: +- [Customer company name]
+ - Action items: + - + - + +Comments: + +``` diff --git a/company/documentation/engineering/oncall.mdx b/company/documentation/engineering/oncall.mdx new file mode 100644 index 0000000000..bab6418457 --- /dev/null +++ b/company/documentation/engineering/oncall.mdx @@ -0,0 +1,78 @@ +--- +title: "On call rotation" +sidebarTitle: "On call rotation" +description: "Learn about call rotation at Infisical" +--- + +Infisical is mission-critical software, which means minimizing service disruptions is a top priority. +To make sure we can react to any issues that come up, we have an on-call rotation that helps us to provide responsive, 24x7x365 support to our customers. +Being part of the on-call rotation is an opportunity to deepen the understanding of our infrastructure, deployment pipelines, and customer-facing systems. +Having this broader understanding of our system not only helps us design better software but also enhances the overall stability of our platform. + +### On-Call Overview + +**Rotation Details** + +Each engineer will be on call once a week, from **Thursday to Thursday**, including weekends. +During this time, the on-call engineer is expected to be available at all times to respond to service disruption alerts. + +While being on call, you are responsible for acting as the first line of defense for critical incidents and answering customer support inquiries. +During your working hours, you must respond to all support tickets or involve relevant team members with sufficient context. +Outside of working hours, you are expected to be available for any high-severity pager alerts and critical support inquiries by customers. + +### Responsibilities While On Call + +During your working hours, prioritize the following in this order: + +1. **Responding to Alerts:** + - Monitor and respond promptly to all PagerDuty alerts. + - Investigate incidents, determine root causes, and mitigate issues. + - Refer to runbooks or any relevant documentation to resolve alarms quickly. +2. **Customer Support:** + - Actively monitor all support inquiries in [**Pylon**](https://app.usepylon.com/issues) and respond to incoming tickets. + - Debug and resolve customer issues. If you encounter a problem outside your expertise, collaborate with the relevant teammates to resolve it. This is an opportunity to learn and build context for future incidents. +3. **Sprint work:** + - Since the current on-call workload does not require all of your working hours, you are expected to work on the sprint items assigned to you. + If the on-call workload increases significantly, inform Maidul to make adjustments. +4. **Continuous Improvement:** + - Take note of recurring patterns, inefficiencies, and opportunities where we can automate to reduce on-call burdens in the future. + + + Outside of working hours, you are expected to be available and respond to any high-severity pager alerts and critical support inquiries by customers. + + +### Before You Get On Call + +- **Set Up PagerDuty:** Ensure you have the PagerDuty mobile app installed, configured, and notifications enabled for Infisical services. +- **Access Required Tools:** Verify access to internal network, runbooks on Notion, [https://grafana.infisical.com](https://grafana.infisical.com/), access to aws accounts and any other access you may require. +- **AWS Permissions:** You will be granted sufficient AWS permissions before the start of your on-call shift in case you need to access production accounts. + +### At the End of Your Shift + +- Post an on-call summary in the Slack channel `#on-call-summaries` at the end of your shift using the following [template](/documentation/engineering/oncall-summery-template). Include notable findings, support inquires and incidents you encountered. + This will helps the rest of the team stay in the loop and open discussions on how to prevent similar issues in the future. +- Do a **handoff meeting/slack huddle** with the next engineer on call to summarize any outstanding work, unresolved issues, or any incidents that require follow-up. Ensure the next on-call engineer is fully briefed so they can pick up where you left off. **Include Maidul in this hand off call.** + +### When to escalate an incident + +If you are paged for incident that you cannot resolve after attempting to debug and mitigate the issue, you should not hesitate to escalate and page others in. +It’s better to get help sooner rather than later to minimize the impact on customers. + +- **Paging relevant teammate:** If you’ve tried resolving an issue on your own and need additional help, page another engineer who might be relevant through PagerDuty. +- **Escalating to Maidul:** You can page Maidul at any time if you think it would be helpful. + +### How to be successful on you rotations + +- Be on top of all changes that get merged into main. This will help you be aware of any changes that might cause issues. +- When responding to support inquiries, double check your replies and make sure they are well written and typo-free. Always acknowledge inquiries quickly to make customers feel valued, and suggest a meeting or huddle if you need more clarity on their issues. +- When customers raise support inquiries, always consider what could have been done to make the inquiry self-serve. Could adding a tooltip next to the relevant feature provide clarity? Maybe the documentation could be more detailed or better organized? +- Document all of your notable support/findings/incidents/feature requests during on call so that it is easy to create your on call summary at the end of your on call shift. + +### Resources + +- [Pylon for support tickets](https://app.usepylon.com/issues) +- [AWS Portal](https://infisical.awsapps.com/start/) +- [View metrics on Grafana](https://grafana.infisical.com/) +- [Metabase](https://analytics.internal.infisical.com/) +- [Run books](https://www.notion.so/Runbooks-19e534883d6b4621b8c712194edbb687?pvs=21) +- [On call summary template](/documentation/engineering/oncall-summery-template) \ No newline at end of file diff --git a/company/mint.json b/company/mint.json index 002a414f99..11edf794e6 100644 --- a/company/mint.json +++ b/company/mint.json @@ -62,7 +62,13 @@ "handbook/time-off", "handbook/hiring", "handbook/meetings", - "handbook/talking-to-customers" + "handbook/talking-to-customers", + { + "group": "Engineering", + "pages": [ + "documentation/engineering/oncall" + ] + } ] } ], diff --git a/docs/integrations/platforms/kubernetes/infisical-dynamic-secret-crd.mdx b/docs/integrations/platforms/kubernetes/infisical-dynamic-secret-crd.mdx index 4f543e9590..07d5fd92fa 100644 --- a/docs/integrations/platforms/kubernetes/infisical-dynamic-secret-crd.mdx +++ b/docs/integrations/platforms/kubernetes/infisical-dynamic-secret-crd.mdx @@ -1,6 +1,6 @@ --- sidebarTitle: "InfisicalDynamicSecret CRD" -title: "InfisicalDynamicSecret CRD" +title: "Using the InfisicalDynamicSecret CRD" description: "Learn how to generate dynamic secret leases in Infisical and sync them to your Kubernetes cluster." --- ## Overview @@ -15,8 +15,10 @@ This CRD offers the following features: - **Optionally trigger redeployments** of any workloads that consume the secret if you enable auto-reload. ### Prerequisites -- The operator is installed on to your Kubernetes cluster -- You have already configured a dynamic secret in Infisical +- A project within Infisical. +- A [machine identity](/docs/documentation/platform/identities/overview) ready for use in Infisical that has permissions to create dynamic secret leases in the project. +- You have already configured a dynamic secret in Infisical. +- The operator is installed on to your Kubernetes cluster. ## Configure Dynamic Secret CRD diff --git a/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx b/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx index eb226e7b84..67fc7f2a8a 100644 --- a/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx +++ b/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx @@ -5,10 +5,22 @@ description: "Learn how to use the InfisicalPushSecret CRD to push and manage se --- -## Push Secrets to Infisical +## Overview +The **InfisicalPushSecret** CRD allows you to create secrets in your Kubernetes cluster and push them to Infisical. -### Example usage + +This CRD offers the following features: +- **Push Secrets** from a Kubernetes secret into Infisical. +- **Manage secret lifecycle** of pushed secrets in Infisical. When the Kubernetes secret is updated, the operator will automatically update the secrets in Infisical. Optionally, when the Kubernetes secret is deleted, the operator will delete the secrets in Infisical automatically. + +### Prerequisites + +- A project within Infisical. +- A [machine identity](/docs/documentation/platform/identities/overview) ready for use in Infisical that has permissions to create secrets in your project. +- The operator is installed on to your Kubernetes cluster. + +## Example usage Below is a sample InfisicalPushSecret CRD that pushes secrets defined in a Kubernetes secret to Infisical. @@ -89,7 +101,7 @@ After applying the soruce-secret.yaml file, you are ready to apply the Infisical After applying the InfisicalPushSecret CRD, you should notice that the secrets you have defined in your source-secret.yaml file have been pushed to your specified destination in Infisical. -### InfisicalPushSecret CRD properties +## InfisicalPushSecret CRD properties If you are fetching secrets from a self-hosted instance of Infisical set the value of `hostAPI` to @@ -272,7 +284,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y - The Kubernetes machine identity authentication method is used to authenticate with Infisical. The identity ID is stored in a field in the InfisicalSecret resource. This authentication method can only be used within a Kubernetes environment. + The Kubernetes machine identity authentication method is used to authenticate with Infisical. The identity ID is stored in a field in the InfisicalPushSecret resource. This authentication method can only be used within a Kubernetes environment. [Read more about Kubernetes Auth](/documentation/platform/identities/kubernetes-auth). Valid fields: - `identityId`: The identity ID of the machine identity you created. @@ -326,7 +338,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y ``` - The GCP IAM machine identity authentication method is used to authenticate with Infisical. The identity ID is stored in a field in the InfisicalSecret resource. This authentication method can only be used both within and outside GCP environments. + The GCP IAM machine identity authentication method is used to authenticate with Infisical. The identity ID is stored in a field in the InfisicalPushSecret resource. This authentication method can only be used both within and outside GCP environments. [Read more about Azure Auth](/documentation/platform/identities/gcp-auth). @@ -344,7 +356,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y ``` - The GCP ID Token machine identity authentication method is used to authenticate with Infisical. The identity ID is stored in a field in the InfisicalSecret resource. This authentication method can only be used within GCP environments. + The GCP ID Token machine identity authentication method is used to authenticate with Infisical. The identity ID is stored in a field in the InfisicalPushSecret resource. This authentication method can only be used within GCP environments. [Read more about Azure Auth](/documentation/platform/identities/gcp-auth). Valid fields: @@ -389,7 +401,7 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y -### Applying the InfisicalPushSecret CRD to your cluster +## Applying the InfisicalPushSecret CRD to your cluster Once you have configured the `InfisicalPushSecret` CRD with the required fields, you can apply it to your cluster. After applying, you should notice that the secrets have been pushed to Infisical. diff --git a/docs/integrations/platforms/kubernetes/infisical-secret-crd.mdx b/docs/integrations/platforms/kubernetes/infisical-secret-crd.mdx index 39154a7c46..f263b518a5 100644 --- a/docs/integrations/platforms/kubernetes/infisical-secret-crd.mdx +++ b/docs/integrations/platforms/kubernetes/infisical-secret-crd.mdx @@ -1,6 +1,6 @@ --- sidebarTitle: "InfisicalSecret CRD" -title: "InfisicalSecret CRD" +title: "Using the InfisicalSecret CRD" description: "Learn how to use the InfisicalSecret CRD to fetch secrets from Infisical and store them as native Kubernetes secret resource" --- diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx index 8f902c5065..8eda21eddf 100644 --- a/docs/self-hosting/configuration/envars.mdx +++ b/docs/self-hosting/configuration/envars.mdx @@ -34,6 +34,27 @@ Used to configure platform-specific security and operational settings this to `false`. +## CORS + +Cross-Origin Resource Sharing (CORS) is a security feature that allows web applications running on one domain to access resources from another domain. +The following environment variables can be used to configure the Infisical Rest API to allow or restrict access to resources from different origins. + + + + Specify a list of origins that are allowed to access the Infisical API. + + An example value would be `CORS_ALLOWED_ORIGINS=["https://example.com"]`. + + Defaults to the same value as your `SITE_URL` environment variable. + + + + Array of HTTP methods allowed for CORS requests. + + Defaults to reflecting the headers specified in the request's Access-Control-Request-Headers header. + + + ## Data Layer The platform utilizes Postgres to persist all of its data and Redis for caching and backgroud tasks @@ -72,7 +93,7 @@ DB_READ_REPLICAS=[{"DB_CONNECTION_URI":""}] -## Email service +## Email Service Without email configuration, Infisical's core functions like sign-up/login and secret operations work, but this disables multi-factor authentication, email invites for projects, alerts for suspicious logins, and all other email-dependent features. diff --git a/frontend/src/pages/organization/UserDetailsByIDPage/components/UserDetailsSection.tsx b/frontend/src/pages/organization/UserDetailsByIDPage/components/UserDetailsSection.tsx index 223340ea4f..cd430deddb 100644 --- a/frontend/src/pages/organization/UserDetailsByIDPage/components/UserDetailsSection.tsx +++ b/frontend/src/pages/organization/UserDetailsByIDPage/components/UserDetailsSection.tsx @@ -133,7 +133,7 @@ export const UserDetailsSection = ({ membershipId, handlePopUpOpen }: Props) => variant="plain" className="group relative ml-2" onClick={() => { - navigator.clipboard.writeText(""); + navigator.clipboard.writeText(membership.user.username); setCopyTextUsername("Copied"); }} >