diff --git a/README.md b/README.md index c64254de7..7e3f40d52 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,8 @@ We're on a mission to make security tooling more accessible to everyone, not jus ### Key Management (KMS): -- **[Cryptograhic Keys](https://infisical.com/docs/documentation/platform/kms)**: Manage and perform cryptographic operations with personally managed keys. +- **[Cryptograhic Keys](https://infisical.com/docs/documentation/platform/kms)**: Centrally manage keys across projects through a user-friendly interface or via the API. - **[Encrypt and Decrypt Data](https://infisical.com/docs/documentation/platform/kms#guide-to-encrypting-data)**: Use symmetric keys to encrypt and decrypt data. -- **[Audit Trail](https://infisical.com/docs/documentation/platform/kms)**: Maintain detailed logs of all key-related activities for compliance and security analysis. ### General Platform: - **Authentication Methods**: Authenticate machine identities with Infisical using a cloud-native or platform agnostic authentication method ([Kubernetes Auth](https://infisical.com/docs/documentation/platform/identities/kubernetes-auth), [GCP Auth](https://infisical.com/docs/documentation/platform/identities/gcp-auth), [Azure Auth](https://infisical.com/docs/documentation/platform/identities/azure-auth), [AWS Auth](https://infisical.com/docs/documentation/platform/identities/aws-auth), [OIDC Auth](https://infisical.com/docs/documentation/platform/identities/oidc-auth/general), [Universal Auth](https://infisical.com/docs/documentation/platform/identities/universal-auth)). diff --git a/backend/src/db/migrations/20240924172713_add-kms-key-project-id.ts b/backend/src/db/migrations/20240924172713_add-kms-key-project-id.ts index abf74605a..6b701eea4 100644 --- a/backend/src/db/migrations/20240924172713_add-kms-key-project-id.ts +++ b/backend/src/db/migrations/20240924172713_add-kms-key-project-id.ts @@ -1,5 +1,6 @@ import { Knex } from "knex"; +import { dropConstraintIfExists } from "@app/db/migrations/utils/dropConstraintIfExists"; import { TableName } from "@app/db/schemas"; export async function up(knex: Knex): Promise { @@ -7,12 +8,14 @@ export async function up(knex: Knex): Promise { const hasOrgId = await knex.schema.hasColumn(TableName.KmsKey, "orgId"); const hasSlug = await knex.schema.hasColumn(TableName.KmsKey, "slug"); + // drop constraint if exists (won't exist if rolled back, see below) + await dropConstraintIfExists(TableName.KmsKey, "kms_keys_orgid_slug_unique", knex); + // projectId for CMEK functionality await knex.schema.alterTable(TableName.KmsKey, (table) => { table.string("projectId").nullable().references("id").inTable(TableName.Project).onDelete("CASCADE"); if (hasOrgId) { - table.dropUnique(["orgId", "slug"]); // prevents using the same key name in different projects so swapping constraint table.unique(["orgId", "projectId", "slug"]); } @@ -36,7 +39,6 @@ export async function down(knex: Knex): Promise { if (hasOrgId) { table.dropUnique(["orgId", "projectId", "slug"]); - table.unique(["orgId", "slug"]); } table.dropColumn("projectId"); }); diff --git a/backend/src/db/migrations/utils/dropConstraintIfExists.ts b/backend/src/db/migrations/utils/dropConstraintIfExists.ts new file mode 100644 index 000000000..bfe487d49 --- /dev/null +++ b/backend/src/db/migrations/utils/dropConstraintIfExists.ts @@ -0,0 +1,6 @@ +import { Knex } from "knex"; + +import { TableName } from "@app/db/schemas"; + +export const dropConstraintIfExists = (tableName: TableName, constraintName: string, knex: Knex) => + knex.raw(`ALTER TABLE ${tableName} DROP CONSTRAINT IF EXISTS ${constraintName};`); diff --git a/backend/src/ee/services/external-kms/providers/aws-kms.ts b/backend/src/ee/services/external-kms/providers/aws-kms.ts index 03d4bac84..6d9166a3a 100644 --- a/backend/src/ee/services/external-kms/providers/aws-kms.ts +++ b/backend/src/ee/services/external-kms/providers/aws-kms.ts @@ -2,24 +2,13 @@ import { CreateKeyCommand, DecryptCommand, DescribeKeyCommand, EncryptCommand, K import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; import { randomUUID } from "crypto"; -import { getConfig } from "@app/lib/config/env"; - import { ExternalKmsAwsSchema, KmsAwsCredentialType, TExternalKmsAwsSchema, TExternalKmsProviderFns } from "./model"; const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => { - const appCfg = getConfig(); - if (providerInputs.credential.type === KmsAwsCredentialType.AssumeRole) { const awsCredential = providerInputs.credential.data; const stsClient = new STSClient({ - region: providerInputs.awsRegion, - credentials: - appCfg.CLIENT_ID_AWS_INTEGRATION && appCfg.CLIENT_SECRET_AWS_INTEGRATION - ? { - accessKeyId: appCfg.CLIENT_ID_AWS_INTEGRATION, - secretAccessKey: appCfg.CLIENT_SECRET_AWS_INTEGRATION - } - : undefined + region: providerInputs.awsRegion }); const command = new AssumeRoleCommand({ RoleArn: awsCredential.assumeRoleArn, diff --git a/backend/src/lib/base64/index.ts b/backend/src/lib/base64/index.ts index fe311cd84..cfc0fde3f 100644 --- a/backend/src/lib/base64/index.ts +++ b/backend/src/lib/base64/index.ts @@ -24,17 +24,5 @@ export const isBase64 = ( }; export const getBase64SizeInBytes = (base64String: string) => { - // Remove data URI scheme if present - const base64 = base64String.replace(/^data:.*?;base64,/, ""); - - // Remove padding characters - const withoutPadding = base64.replace(/=+$/, ""); - - // Calculate bits: each base64 character represents 6 bits - const totalBits = withoutPadding.length * 6; - - // Convert bits to bytes (8 bits = 1 byte) - const bytes = totalBits / 8; - - return bytes; + return Buffer.from(base64String, "base64").length; }; diff --git a/backend/src/server/routes/v1/cmek-router.ts b/backend/src/server/routes/v1/cmek-router.ts index 44b3256a0..b07d7b490 100644 --- a/backend/src/server/routes/v1/cmek-router.ts +++ b/backend/src/server/routes/v1/cmek-router.ts @@ -31,10 +31,10 @@ const base64Schema = z.string().superRefine((val, ctx) => { }); } - if (getBase64SizeInBytes(val) > 6144) { + if (getBase64SizeInBytes(val) > 4096) { ctx.addIssue({ code: z.ZodIssueCode.custom, - message: "data cannot exceed 6144 bytes" + message: "data cannot exceed 4096 bytes" }); } }); @@ -65,7 +65,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { body: { projectId, name, description, encryptionAlgorithm }, @@ -118,7 +118,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { params: { keyId }, @@ -162,7 +162,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { params: { keyId }, @@ -214,7 +214,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { query: { projectId, ...dto }, @@ -259,7 +259,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { params: { keyId }, @@ -304,7 +304,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { params: { keyId }, diff --git a/backend/src/services/kms/kms-service.ts b/backend/src/services/kms/kms-service.ts index 93f5ff4db..40ec3699a 100644 --- a/backend/src/services/kms/kms-service.ts +++ b/backend/src/services/kms/kms-service.ts @@ -67,16 +67,6 @@ export const kmsServiceFactory = ({ }: TKmsServiceFactoryDep) => { let ROOT_ENCRYPTION_KEY = Buffer.alloc(0); - const $getRootEncryptionKey = (encryptionAlgorithm: SymmetricEncryption) => { - switch (encryptionAlgorithm) { - case SymmetricEncryption.AES_GCM_128: - return ROOT_ENCRYPTION_KEY.subarray(0, 16); // taking first 128bits - case SymmetricEncryption.AES_GCM_256: - default: - return ROOT_ENCRYPTION_KEY; - } - }; - /* * Generate KMS Key * This function is responsibile for generating the infisical internal KMS for various entities @@ -91,10 +81,11 @@ export const kmsServiceFactory = ({ encryptionAlgorithm = SymmetricEncryption.AES_GCM_256, description }: TGenerateKMSDTO) => { - const cipher = symmetricCipherService(encryptionAlgorithm); + const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256); const kmsKeyMaterial = randomSecureBytes(getByteLengthForAlgorithm(encryptionAlgorithm)); - const encryptedKeyMaterial = cipher.encrypt(kmsKeyMaterial, $getRootEncryptionKey(encryptionAlgorithm)); + + const encryptedKeyMaterial = cipher.encrypt(kmsKeyMaterial, ROOT_ENCRYPTION_KEY); const sanitizedName = name ? slugify(name) : slugify(alphaNumericNanoId(8).toLowerCase()); const dbQuery = async (db: Knex) => { const kmsDoc = await kmsDAL.create( @@ -308,17 +299,13 @@ export const kmsServiceFactory = ({ } // internal KMS - const encryptionAlgorithm = - (kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption) ?? SymmetricEncryption.AES_GCM_256; - const cipher = symmetricCipherService(encryptionAlgorithm); - const kmsKey = cipher.decrypt( - kmsDoc.internalKms?.encryptedKey as Buffer, - $getRootEncryptionKey(encryptionAlgorithm) - ); + const keyCipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256); + const dataCipher = symmetricCipherService(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption); + const kmsKey = keyCipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY); return ({ cipherTextBlob: versionedCipherTextBlob }: Pick) => { const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH); - const decryptedBlob = cipher.decrypt(cipherTextBlob, kmsKey); + const decryptedBlob = dataCipher.decrypt(cipherTextBlob, kmsKey); return Promise.resolve(decryptedBlob); }; }; @@ -374,15 +361,11 @@ export const kmsServiceFactory = ({ } // internal KMS - const encryptionAlgorithm = - (kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption) ?? SymmetricEncryption.AES_GCM_256; - const cipher = symmetricCipherService(encryptionAlgorithm); + const keyCipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256); + const dataCipher = symmetricCipherService(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption); return ({ plainText }: Pick) => { - const kmsKey = cipher.decrypt( - kmsDoc.internalKms?.encryptedKey as Buffer, - $getRootEncryptionKey(encryptionAlgorithm) - ); - const encryptedPlainTextBlob = cipher.encrypt(plainText, kmsKey); + const kmsKey = keyCipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY); + const encryptedPlainTextBlob = dataCipher.encrypt(plainText, kmsKey); // Buffer#1 encrypted text + Buffer#2 version number const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3 diff --git a/docs/documentation/platform/identities/aws-auth.mdx b/docs/documentation/platform/identities/aws-auth.mdx index 5c82a3807..494606ccd 100644 --- a/docs/documentation/platform/identities/aws-auth.mdx +++ b/docs/documentation/platform/identities/aws-auth.mdx @@ -7,7 +7,7 @@ description: "Learn how to authenticate with Infisical for EC2 instances, Lambda ## Diagram -The following sequence digram illustrates the AWS Auth workflow for authenticating AWS IAM principals with Infisical. +The following sequence diagram illustrates the AWS Auth workflow for authenticating AWS IAM principals with Infisical. ```mermaid sequenceDiagram diff --git a/docs/documentation/platform/identities/azure-auth.mdx b/docs/documentation/platform/identities/azure-auth.mdx index 910ca1014..03d997ffb 100644 --- a/docs/documentation/platform/identities/azure-auth.mdx +++ b/docs/documentation/platform/identities/azure-auth.mdx @@ -7,7 +7,7 @@ description: "Learn how to authenticate with Infisical for services on Azure" ## Diagram -The following sequence digram illustrates the Azure Auth workflow for authenticating Azure [service principals](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser) with Infisical. +The following sequence diagram illustrates the Azure Auth workflow for authenticating Azure [service principals](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser) with Infisical. ```mermaid sequenceDiagram diff --git a/docs/documentation/platform/identities/gcp-auth.mdx b/docs/documentation/platform/identities/gcp-auth.mdx index 09b41852d..6573544de 100644 --- a/docs/documentation/platform/identities/gcp-auth.mdx +++ b/docs/documentation/platform/identities/gcp-auth.mdx @@ -13,7 +13,7 @@ description: "Learn how to authenticate with Infisical for services on Google Cl ## Diagram - The following sequence digram illustrates the GCP ID Token Auth workflow for authenticating GCP resources with Infisical. + The following sequence diagram illustrates the GCP ID Token Auth workflow for authenticating GCP resources with Infisical. ```mermaid sequenceDiagram @@ -182,7 +182,7 @@ access the Infisical API using the GCP ID Token authentication method. ## Diagram - The following sequence digram illustrates the GCP IAM Auth workflow for authenticating GCP IAM service accounts with Infisical. + The following sequence diagram illustrates the GCP IAM Auth workflow for authenticating GCP IAM service accounts with Infisical. ```mermaid sequenceDiagram diff --git a/docs/documentation/platform/identities/kubernetes-auth.mdx b/docs/documentation/platform/identities/kubernetes-auth.mdx index 153b0f4a3..b4d7cc1ac 100644 --- a/docs/documentation/platform/identities/kubernetes-auth.mdx +++ b/docs/documentation/platform/identities/kubernetes-auth.mdx @@ -7,7 +7,7 @@ description: "Learn how to authenticate with Infisical in Kubernetes" ## Diagram - The following sequence digram illustrates the Kubernetes Auth workflow for authenticating applications running in pods with Infisical. + The following sequence diagram illustrates the Kubernetes Auth workflow for authenticating applications running in pods with Infisical. ```mermaid sequenceDiagram diff --git a/docs/documentation/platform/identities/token-auth.mdx b/docs/documentation/platform/identities/token-auth.mdx index 61906542f..59c5f9abf 100644 --- a/docs/documentation/platform/identities/token-auth.mdx +++ b/docs/documentation/platform/identities/token-auth.mdx @@ -7,7 +7,7 @@ description: "Learn how to authenticate to Infisical from any platform or enviro ## Diagram -The following sequence digram illustrates the Token Auth workflow for authenticating clients with Infisical. +The following sequence diagram illustrates the Token Auth workflow for authenticating clients with Infisical. ```mermaid sequenceDiagram diff --git a/docs/documentation/platform/identities/universal-auth.mdx b/docs/documentation/platform/identities/universal-auth.mdx index 32e916631..597978093 100644 --- a/docs/documentation/platform/identities/universal-auth.mdx +++ b/docs/documentation/platform/identities/universal-auth.mdx @@ -7,7 +7,7 @@ description: "Learn how to authenticate to Infisical from any platform or enviro ## Diagram -The following sequence digram illustrates the Universal Auth workflow for authenticating clients with Infisical. +The following sequence diagram illustrates the Universal Auth workflow for authenticating clients with Infisical. ```mermaid sequenceDiagram diff --git a/docs/documentation/platform/kms-configuration/overview.mdx b/docs/documentation/platform/kms-configuration/overview.mdx index abdfbe847..327481bc4 100644 --- a/docs/documentation/platform/kms-configuration/overview.mdx +++ b/docs/documentation/platform/kms-configuration/overview.mdx @@ -25,9 +25,4 @@ For existing projects, you can configure the KMS from the Project Settings page. ## External KMS -Infisical supports the use of external KMS solutions to enhance security and compliance. You can configure your project to use services like [AWS Key Management Service](./aws-kms) for managing encryption. - -## Infisical KMS - -Infisical exposes it's internal KMS solution, [Infisical KMS](../kms), enabling you to create and manage keys to perform cryptographic operations with. - +Infisical supports the use of external KMS solutions to enhance security and compliance. You can configure your project to use services like [AWS Key Management Service](./aws-kms) for managing encryption. \ No newline at end of file diff --git a/docs/documentation/platform/kms.mdx b/docs/documentation/platform/kms.mdx index 2774148ac..d3329a08f 100644 --- a/docs/documentation/platform/kms.mdx +++ b/docs/documentation/platform/kms.mdx @@ -4,20 +4,43 @@ sidebarTitle: "Key Management (KMS)" description: "Learn how to manage and use cryptographic keys with Infisical." --- -## Introduction +## Diagram -Infisical's Key Management System (KMS) allows you to create, store and manage cryptographic keys. -These keys can be used to perform cryptographic operations such as data encryption. You can access -Infisical's KMS from the [project](./project) sidebar. +The following sequence diagram illustrates the KMS workflow for creating and using a cryptographic key. -## Features +
+```mermaid +sequenceDiagram + participant Client as Client + participant Infis as Infisical -1. Centralized Key Storage: Securely store all your organization's cryptographic keys in one location. -2. Encryption and - Decryption: Provide on-demand encryption and decryption services without exposing the keys to -external applications. -3. Audit - Trails: Maintain detailed logs of all key-related activities for compliance and security analysis. + Note over Client,Infis: Step 1: Create KMS Key + Client->>Infis: create key request + Infis->>Client: keyId + + Note over Client,Infis: Step 2: Encrypt Data + Client->>Infis: plaintext and keyId + Infis->>Client: ciphertext + + Note over Client,Infis: Step 3: Decrypt Data + Client->>Infis: ciphertext and keyId + Infis->>Client: plaintext +``` +
+ +## Concept + +At a high-level, Infisical generates a KMS key when requested, returning the `keyId` to the requester. This `keyId` can then be used +to perform cryptographic operations such as encrypting and decrypting data. + +To be more specific: + +1. The client requests to create a key using the `/api/v1/kms/keys` endpoint. +2. Infisical generates a KMS key and returns the `keyId` to the requester. +3. The client requests to encrypt `plaintext` data (base64 encoded) with the specified `keyId` using the `/api/v1/kms/keys//encrypt` endpoint. +4. Infisical returns the encrypted data or `ciphertext` (base64 encoded). +3. The client requests to decrypt the `ciphertext` data with the original `keyId` using the `/api/v1/kms/keys//decrypt` endpoint. +4. Infisical returns the decrypted `plaintext` data (base64 encoded). Your keys will never be used or viewable outside of Infisical KMS. @@ -38,7 +61,8 @@ In the following steps, we'll explore how to generate a cryptographic key and en Specify your key details. Here's some guidance on each field: - Name: A slug-friendly name for the key. - - Type: The encryption algorithm associated with this key. By default symmetric `AES-GCM-256` is selected, + - Type: The encryption algorithm associated with this key. By default symmetric `AES-GCM-256` is + selected, but Infisical will continue to add more options down the road. - Description: An optional description of what this key is used for. @@ -75,11 +99,11 @@ In the following steps, we'll explore how to generate a cryptographic key and en --url https://app.infisical.com/api/v1/kms/keys \ --header 'Content-Type: application/json' \ --data '{ - "projectId": "", - "name": "my-secret-key", - "description": "...", - "encryptionAlgorithm": "aes-256-gcm" - }' + "projectId": "", + "name": "my-secret-key", + "description": "...", + "encryptionAlgorithm": "aes-256-gcm" + }' ``` ### Sample response @@ -113,20 +137,21 @@ In the following steps, we'll explore how to generate a cryptographic key and en ```bash Request curl --request POST \ - --url https://app.infisical.com/api/v1/kms/keys//encrypt \ - --header 'Content-Type: application/json' \ - --data '{ - "plaintext": "lUFHM5Ggwo6TOfpuN1S==" // base64 encoded plaintext - }' - ``` + --url https://app.infisical.com/api/v1/kms/keys/ + /encrypt \ + --header 'Content-Type: application/json' \ + --data '{ + "plaintext": "lUFHM5Ggwo6TOfpuN1S==" // base64 encoded plaintext + }' + ``` - ### Sample response + ### Sample response - ```bash Response - { - "ciphertext": "HwFHwSFHwlMF6TOfp==" // base64 encoded ciphertext - } - ``` + ```bash Response + { + "ciphertext": "HwFHwSFHwlMF6TOfp==" // base64 encoded ciphertext + } + ``` @@ -168,20 +193,21 @@ In the following steps, we'll explore how to decrypt data. ```bash Request curl --request POST \ - --url https://app.infisical.com/api/v1/kms/keys//decrypt \ - --header 'Content-Type: application/json' \ - --data '{ - "ciphertext": "HwFHwSFHwlMF6TOfp==" // base64 encoded ciphertext - }' - ``` + --url https://app.infisical.com/api/v1/kms/keys/ + /decrypt \ + --header 'Content-Type: application/json' \ + --data '{ + "ciphertext": "HwFHwSFHwlMF6TOfp==" // base64 encoded ciphertext + }' + ``` - ### Sample response + ### Sample response - ```bash Response - { - "plaintext": "lUFHM5Ggwo6TOfpuN1S==" // base64 encoded plaintext - } - ``` + ```bash Response + { + "plaintext": "lUFHM5Ggwo6TOfpuN1S==" // base64 encoded plaintext + } + ``` @@ -197,4 +223,8 @@ In the following steps, we'll explore how to decrypt data. No. Infisical's KMS will never expose your keys, encrypted or decrypted, to external sources. + + Currently, Infisical only supports AES-128-GCM and AES-256-GCM for encryption operations. We anticipate + supporting more algorithms and cryptographic operations in the coming months. +