improvements: address requested changes

This commit is contained in:
Scott Wilson
2024-10-03 12:55:53 -07:00
parent 7cd85cf84a
commit c66a711890
15 changed files with 111 additions and 119 deletions
+1 -2
View File
@@ -75,9 +75,8 @@ We're on a mission to make security tooling more accessible to everyone, not jus
### Key Management (KMS): ### 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. - **[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: ### 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)). - **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)).
@@ -1,5 +1,6 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { dropConstraintIfExists } from "@app/db/migrations/utils/dropConstraintIfExists";
import { TableName } from "@app/db/schemas"; import { TableName } from "@app/db/schemas";
export async function up(knex: Knex): Promise<void> { export async function up(knex: Knex): Promise<void> {
@@ -7,12 +8,14 @@ export async function up(knex: Knex): Promise<void> {
const hasOrgId = await knex.schema.hasColumn(TableName.KmsKey, "orgId"); const hasOrgId = await knex.schema.hasColumn(TableName.KmsKey, "orgId");
const hasSlug = await knex.schema.hasColumn(TableName.KmsKey, "slug"); 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 // projectId for CMEK functionality
await knex.schema.alterTable(TableName.KmsKey, (table) => { await knex.schema.alterTable(TableName.KmsKey, (table) => {
table.string("projectId").nullable().references("id").inTable(TableName.Project).onDelete("CASCADE"); table.string("projectId").nullable().references("id").inTable(TableName.Project).onDelete("CASCADE");
if (hasOrgId) { if (hasOrgId) {
table.dropUnique(["orgId", "slug"]); // prevents using the same key name in different projects so swapping constraint
table.unique(["orgId", "projectId", "slug"]); table.unique(["orgId", "projectId", "slug"]);
} }
@@ -36,7 +39,6 @@ export async function down(knex: Knex): Promise<void> {
if (hasOrgId) { if (hasOrgId) {
table.dropUnique(["orgId", "projectId", "slug"]); table.dropUnique(["orgId", "projectId", "slug"]);
table.unique(["orgId", "slug"]);
} }
table.dropColumn("projectId"); table.dropColumn("projectId");
}); });
@@ -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};`);
@@ -2,24 +2,13 @@ import { CreateKeyCommand, DecryptCommand, DescribeKeyCommand, EncryptCommand, K
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import { randomUUID } from "crypto"; import { randomUUID } from "crypto";
import { getConfig } from "@app/lib/config/env";
import { ExternalKmsAwsSchema, KmsAwsCredentialType, TExternalKmsAwsSchema, TExternalKmsProviderFns } from "./model"; import { ExternalKmsAwsSchema, KmsAwsCredentialType, TExternalKmsAwsSchema, TExternalKmsProviderFns } from "./model";
const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => { const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => {
const appCfg = getConfig();
if (providerInputs.credential.type === KmsAwsCredentialType.AssumeRole) { if (providerInputs.credential.type === KmsAwsCredentialType.AssumeRole) {
const awsCredential = providerInputs.credential.data; const awsCredential = providerInputs.credential.data;
const stsClient = new STSClient({ const stsClient = new STSClient({
region: providerInputs.awsRegion, 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
}); });
const command = new AssumeRoleCommand({ const command = new AssumeRoleCommand({
RoleArn: awsCredential.assumeRoleArn, RoleArn: awsCredential.assumeRoleArn,
+1 -13
View File
@@ -24,17 +24,5 @@ export const isBase64 = (
}; };
export const getBase64SizeInBytes = (base64String: string) => { export const getBase64SizeInBytes = (base64String: string) => {
// Remove data URI scheme if present return Buffer.from(base64String, "base64").length;
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;
}; };
+8 -8
View File
@@ -31,10 +31,10 @@ const base64Schema = z.string().superRefine((val, ctx) => {
}); });
} }
if (getBase64SizeInBytes(val) > 6144) { if (getBase64SizeInBytes(val) > 4096) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, 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) => { handler: async (req) => {
const { const {
body: { projectId, name, description, encryptionAlgorithm }, 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) => { handler: async (req) => {
const { const {
params: { keyId }, 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) => { handler: async (req) => {
const { const {
params: { keyId }, 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) => { handler: async (req) => {
const { const {
query: { projectId, ...dto }, 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) => { handler: async (req) => {
const { const {
params: { keyId }, 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) => { handler: async (req) => {
const { const {
params: { keyId }, params: { keyId },
+11 -28
View File
@@ -67,16 +67,6 @@ export const kmsServiceFactory = ({
}: TKmsServiceFactoryDep) => { }: TKmsServiceFactoryDep) => {
let ROOT_ENCRYPTION_KEY = Buffer.alloc(0); 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 * Generate KMS Key
* This function is responsibile for generating the infisical internal KMS for various entities * 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, encryptionAlgorithm = SymmetricEncryption.AES_GCM_256,
description description
}: TGenerateKMSDTO) => { }: TGenerateKMSDTO) => {
const cipher = symmetricCipherService(encryptionAlgorithm); const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
const kmsKeyMaterial = randomSecureBytes(getByteLengthForAlgorithm(encryptionAlgorithm)); 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 sanitizedName = name ? slugify(name) : slugify(alphaNumericNanoId(8).toLowerCase());
const dbQuery = async (db: Knex) => { const dbQuery = async (db: Knex) => {
const kmsDoc = await kmsDAL.create( const kmsDoc = await kmsDAL.create(
@@ -308,17 +299,13 @@ export const kmsServiceFactory = ({
} }
// internal KMS // internal KMS
const encryptionAlgorithm = const keyCipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption) ?? SymmetricEncryption.AES_GCM_256; const dataCipher = symmetricCipherService(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption);
const cipher = symmetricCipherService(encryptionAlgorithm); const kmsKey = keyCipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY);
const kmsKey = cipher.decrypt(
kmsDoc.internalKms?.encryptedKey as Buffer,
$getRootEncryptionKey(encryptionAlgorithm)
);
return ({ cipherTextBlob: versionedCipherTextBlob }: Pick<TDecryptWithKmsDTO, "cipherTextBlob">) => { return ({ cipherTextBlob: versionedCipherTextBlob }: Pick<TDecryptWithKmsDTO, "cipherTextBlob">) => {
const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH); 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); return Promise.resolve(decryptedBlob);
}; };
}; };
@@ -374,15 +361,11 @@ export const kmsServiceFactory = ({
} }
// internal KMS // internal KMS
const encryptionAlgorithm = const keyCipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption) ?? SymmetricEncryption.AES_GCM_256; const dataCipher = symmetricCipherService(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption);
const cipher = symmetricCipherService(encryptionAlgorithm);
return ({ plainText }: Pick<TEncryptWithKmsDTO, "plainText">) => { return ({ plainText }: Pick<TEncryptWithKmsDTO, "plainText">) => {
const kmsKey = cipher.decrypt( const kmsKey = keyCipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY);
kmsDoc.internalKms?.encryptedKey as Buffer, const encryptedPlainTextBlob = dataCipher.encrypt(plainText, kmsKey);
$getRootEncryptionKey(encryptionAlgorithm)
);
const encryptedPlainTextBlob = cipher.encrypt(plainText, kmsKey);
// Buffer#1 encrypted text + Buffer#2 version number // Buffer#1 encrypted text + Buffer#2 version number
const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3 const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3
@@ -7,7 +7,7 @@ description: "Learn how to authenticate with Infisical for EC2 instances, Lambda
## Diagram ## 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 ```mermaid
sequenceDiagram sequenceDiagram
@@ -7,7 +7,7 @@ description: "Learn how to authenticate with Infisical for services on Azure"
## Diagram ## 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 ```mermaid
sequenceDiagram sequenceDiagram
@@ -13,7 +13,7 @@ description: "Learn how to authenticate with Infisical for services on Google Cl
## Diagram ## 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 ```mermaid
sequenceDiagram sequenceDiagram
@@ -182,7 +182,7 @@ access the Infisical API using the GCP ID Token authentication method.
## Diagram ## 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 ```mermaid
sequenceDiagram sequenceDiagram
@@ -7,7 +7,7 @@ description: "Learn how to authenticate with Infisical in Kubernetes"
## Diagram ## 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 ```mermaid
sequenceDiagram sequenceDiagram
@@ -7,7 +7,7 @@ description: "Learn how to authenticate to Infisical from any platform or enviro
## Diagram ## 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 ```mermaid
sequenceDiagram sequenceDiagram
@@ -7,7 +7,7 @@ description: "Learn how to authenticate to Infisical from any platform or enviro
## Diagram ## 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 ```mermaid
sequenceDiagram sequenceDiagram
@@ -26,8 +26,3 @@ For existing projects, you can configure the KMS from the Project Settings page.
## External KMS ## 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 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.
+44 -14
View File
@@ -4,20 +4,43 @@ sidebarTitle: "Key Management (KMS)"
description: "Learn how to manage and use cryptographic keys with Infisical." description: "Learn how to manage and use cryptographic keys with Infisical."
--- ---
## Introduction ## Diagram
Infisical's <strong>Key Management System (KMS)</strong> allows you to create, store and manage cryptographic keys. The following sequence diagram illustrates the KMS workflow for creating and using a cryptographic key.
These keys can be used to perform cryptographic operations such as data encryption. You can access
Infisical's KMS from the [project](./project) sidebar.
## Features <div align="center">
```mermaid
sequenceDiagram
participant Client as Client
participant Infis as Infisical
1. <strong>Centralized Key Storage:</strong> Securely store all your organization's cryptographic keys in one location. Note over Client,Infis: Step 1: Create KMS Key
2. <strong>Encryption and Client->>Infis: create key request
Decryption:</strong> Provide on-demand encryption and decryption services without exposing the keys to Infis->>Client: keyId
external applications.
3. <strong>Audit Note over Client,Infis: Step 2: Encrypt Data
Trails:</strong> Maintain detailed logs of all key-related activities for compliance and security analysis. Client->>Infis: plaintext and keyId
Infis->>Client: ciphertext
Note over Client,Infis: Step 3: Decrypt Data
Client->>Infis: ciphertext and keyId
Infis->>Client: plaintext
```
</div>
## 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/<key-id>/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/<key-id>/decrypt` endpoint.
4. Infisical returns the decrypted `plaintext` data (base64 encoded).
<Note> <Note>
Your keys will never be used or viewable outside of Infisical KMS. 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: Specify your key details. Here's some guidance on each field:
- Name: A slug-friendly name for the key. - 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 but
Infisical will continue to add more options down the road. Infisical will continue to add more options down the road.
- Description: An optional description of what this key is used for. - Description: An optional description of what this key is used for.
@@ -113,7 +137,8 @@ In the following steps, we'll explore how to generate a cryptographic key and en
```bash Request ```bash Request
curl --request POST \ curl --request POST \
--url https://app.infisical.com/api/v1/kms/keys/<key-id>/encrypt \ --url https://app.infisical.com/api/v1/kms/keys/
<key-id>/encrypt \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--data '{ --data '{
"plaintext": "lUFHM5Ggwo6TOfpuN1S==" // base64 encoded plaintext "plaintext": "lUFHM5Ggwo6TOfpuN1S==" // base64 encoded plaintext
@@ -168,7 +193,8 @@ In the following steps, we'll explore how to decrypt data.
```bash Request ```bash Request
curl --request POST \ curl --request POST \
--url https://app.infisical.com/api/v1/kms/keys/<key-id>/decrypt \ --url https://app.infisical.com/api/v1/kms/keys/
<key-id>/decrypt \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--data '{ --data '{
"ciphertext": "HwFHwSFHwlMF6TOfp==" // base64 encoded ciphertext "ciphertext": "HwFHwSFHwlMF6TOfp==" // base64 encoded ciphertext
@@ -197,4 +223,8 @@ In the following steps, we'll explore how to decrypt data.
<Accordion title="Can key material be accessed outside of Infisical KMS?"> <Accordion title="Can key material be accessed outside of Infisical KMS?">
No. Infisical's KMS will never expose your keys, encrypted or decrypted, to external sources. No. Infisical's KMS will never expose your keys, encrypted or decrypted, to external sources.
</Accordion> </Accordion>
<Accordion title="What algorithms does Infisical KMS support?">
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.
</Accordion>
</AccordionGroup> </AccordionGroup>