diff --git a/backend/src/lib/knex/index.ts b/backend/src/lib/knex/index.ts index d43d2af8e..55d4bf399 100644 --- a/backend/src/lib/knex/index.ts +++ b/backend/src/lib/knex/index.ts @@ -2,6 +2,8 @@ import { Knex } from "knex"; import { Tables } from "knex/types/tables"; +import { TableName } from "@app/db/schemas"; + import { DatabaseError } from "../errors"; import { buildDynamicKnexQuery, TKnexDynamicOperator } from "./dynamic"; @@ -25,28 +27,41 @@ export type TFindFilter = Partial & { $search?: Partial<{ [k in keyof R]: R[k] }>; $complex?: TKnexDynamicOperator; }; + export const buildFindFilter = - ({ $in, $notNull, $search, $complex, ...filter }: TFindFilter) => + ( + { $in, $notNull, $search, $complex, ...filter }: TFindFilter, + tableName?: TableName, + excludeKeys?: Array + ) => (bd: Knex.QueryBuilder) => { - void bd.where(filter); + const processedFilter = tableName + ? Object.fromEntries( + Object.entries(filter) + .filter(([key]) => !excludeKeys || !excludeKeys.includes(key as keyof R)) + .map(([key, value]) => [`${tableName}.${key}`, value]) + ) + : filter; + + void bd.where(processedFilter); if ($in) { Object.entries($in).forEach(([key, val]) => { if (val) { - void bd.whereIn(key as never, val as never); + void bd.whereIn([`${tableName ? `${tableName}.` : ""}${key}`] as never, val as never); } }); } if ($notNull?.length) { $notNull.forEach((key) => { - void bd.whereNotNull(key as never); + void bd.whereNotNull([`${tableName ? `${tableName}.` : ""}${key as string}`] as never); }); } if ($search) { Object.entries($search).forEach(([key, val]) => { if (val) { - void bd.whereILike(key as never, val as never); + void bd.whereILike([`${tableName ? `${tableName}.` : ""}${key}`] as never, val as never); } }); } diff --git a/backend/src/lib/validator/validate-url.ts b/backend/src/lib/validator/validate-url.ts index b555869d7..8f195e0b5 100644 --- a/backend/src/lib/validator/validate-url.ts +++ b/backend/src/lib/validator/validate-url.ts @@ -15,13 +15,13 @@ export const blockLocalAndPrivateIpAddresses = async (url: string) => { const validUrl = new URL(url); const inputHostIps: string[] = []; - if (isIPv4(validUrl.host)) { - inputHostIps.push(validUrl.host); + if (isIPv4(validUrl.hostname)) { + inputHostIps.push(validUrl.hostname); } else { - if (validUrl.host === "localhost" || validUrl.host === "host.docker.internal") { + if (validUrl.hostname === "localhost" || validUrl.hostname === "host.docker.internal") { throw new BadRequestError({ message: "Local IPs not allowed as URL" }); } - const resolvedIps = await dns.resolve4(validUrl.host); + const resolvedIps = await dns.resolve4(validUrl.hostname); inputHostIps.push(...resolvedIps); } const isInternalIp = inputHostIps.some((el) => isPrivateIp(el)); diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 8ceeba648..a71a69c20 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -1541,6 +1541,7 @@ export const registerRoutes = async ( const secretSyncService = secretSyncServiceFactory({ secretSyncDAL, + secretImportDAL, permissionService, appConnectionService, folderDAL, diff --git a/backend/src/server/routes/v1/dashboard-router.ts b/backend/src/server/routes/v1/dashboard-router.ts index 54da97682..373e2d51f 100644 --- a/backend/src/server/routes/v1/dashboard-router.ts +++ b/backend/src/server/routes/v1/dashboard-router.ts @@ -154,7 +154,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { secrets: z .object({ secretId: z.string(), - referencedSecretKey: z.string() + referencedSecretKey: z.string(), + referencedSecretEnv: z.string() }) .array() .optional() @@ -166,6 +167,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { }) .array() .optional(), + usedBySecretSyncs: z + .object({ + name: z.string(), + destination: z.string(), + environment: z.string(), + id: z.string(), + path: z.string() + }) + .array() + .optional(), totalFolderCount: z.number().optional(), totalDynamicSecretCount: z.number().optional(), totalSecretCount: z.number().optional(), @@ -500,6 +511,24 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { } } + const usedBySecretSyncs: { name: string; destination: string; environment: string; id: string; path: string }[] = + []; + for await (const environment of environments) { + const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath( + { projectId, secretPath, environment }, + req.permission + ); + secretSyncs.forEach((sync) => { + usedBySecretSyncs.push({ + name: sync.name, + destination: sync.destination, + environment, + id: sync.id, + path: sync.folder?.path || "/" + }); + }); + } + return { folders, dynamicSecrets, @@ -512,6 +541,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { totalSecretCount, totalSecretRotationCount, importedByEnvs, + usedBySecretSyncs, totalCount: (totalFolderCount ?? 0) + (totalDynamicSecretCount ?? 0) + @@ -611,6 +641,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { totalFolderCount: z.number().optional(), totalDynamicSecretCount: z.number().optional(), totalSecretCount: z.number().optional(), + usedBySecretSyncs: z + .object({ + name: z.string(), + destination: z.string(), + environment: z.string(), + id: z.string(), + path: z.string() + }) + .array() + .optional(), importedBy: z .object({ environment: z.object({ @@ -624,7 +664,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { secrets: z .object({ secretId: z.string(), - referencedSecretKey: z.string() + referencedSecretKey: z.string(), + referencedSecretEnv: z.string() }) .array() .optional() @@ -904,6 +945,18 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { secrets }); + const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath( + { projectId, secretPath, environment }, + req.permission + ); + const usedBySecretSyncs = secretSyncs.map((sync) => ({ + name: sync.name, + destination: sync.destination, + environment: sync.environment?.name || environment, + id: sync.id, + path: sync.folder?.path || "/" + })); + if (secrets?.length || secretRotations?.length) { const secretCount = (secrets?.length ?? 0) + @@ -950,6 +1003,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { totalSecretCount, totalSecretRotationCount, importedBy, + usedBySecretSyncs, totalCount: (totalImportCount ?? 0) + (totalFolderCount ?? 0) + diff --git a/backend/src/services/secret-import/secret-import-dal.ts b/backend/src/services/secret-import/secret-import-dal.ts index 1a171aa2e..dbe2f6a84 100644 --- a/backend/src/services/secret-import/secret-import-dal.ts +++ b/backend/src/services/secret-import/secret-import-dal.ts @@ -171,6 +171,19 @@ export const secretImportDALFactory = (db: TDbClient) => { } }; + const getFolderImports = async (secretPath: string, environmentId: string, tx?: Knex) => { + try { + const folderImports = await (tx || db.replicaNode())(TableName.SecretImport) + .where({ importPath: secretPath, importEnv: environmentId }) + .join(TableName.SecretFolder, `${TableName.SecretImport}.folderId`, `${TableName.SecretFolder}.id`) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) + .select(db.ref("id").withSchema(TableName.SecretFolder).as("folderId")); + return folderImports; + } catch (error) { + throw new DatabaseError({ error, name: "get secret imports" }); + } + }; + const getFolderIsImportedBy = async ( secretPath: string, environmentId: string, @@ -203,7 +216,8 @@ export const secretImportDALFactory = (db: TDbClient) => { db.ref("name").withSchema(TableName.Environment).as("envName"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("id").withSchema(TableName.SecretFolder).as("folderId"), - db.ref("secretKey").withSchema(TableName.SecretReferenceV2).as("referencedSecretKey") + db.ref("secretKey").withSchema(TableName.SecretReferenceV2).as("referencedSecretKey"), + db.ref("environment").withSchema(TableName.SecretReferenceV2).as("referencedSecretEnv") ); const folderResults = folderImports.map(({ envName, envSlug, folderName, folderId }) => ({ @@ -214,13 +228,14 @@ export const secretImportDALFactory = (db: TDbClient) => { })); const secretResults = secretReferences.map( - ({ envName, envSlug, secretId, folderName, folderId, referencedSecretKey }) => ({ + ({ envName, envSlug, secretId, folderName, folderId, referencedSecretKey, referencedSecretEnv }) => ({ envName, envSlug, secretId, folderName, folderId, - referencedSecretKey + referencedSecretKey, + referencedSecretEnv }) ); @@ -235,6 +250,7 @@ export const secretImportDALFactory = (db: TDbClient) => { secrets: { secretId: string; referencedSecretKey: string; + referencedSecretEnv: string; }[]; folderId: string; folderImported: boolean; @@ -264,7 +280,11 @@ export const secretImportDALFactory = (db: TDbClient) => { if ("secretId" in item && item.secretId) { updatedAcc[env].folders[folder].secrets = [ ...updatedAcc[env].folders[folder].secrets, - { secretId: item.secretId, referencedSecretKey: item.referencedSecretKey } + { + secretId: item.secretId, + referencedSecretKey: item.referencedSecretKey, + referencedSecretEnv: item.referencedSecretEnv + } ]; } else { updatedAcc[env].folders[folder].folderImported = true; @@ -309,6 +329,7 @@ export const secretImportDALFactory = (db: TDbClient) => { findLastImportPosition, updateAllPosition, getProjectImportCount, - getFolderIsImportedBy + getFolderIsImportedBy, + getFolderImports }; }; diff --git a/backend/src/services/secret-import/secret-import-service.ts b/backend/src/services/secret-import/secret-import-service.ts index 2015516f5..5078496d6 100644 --- a/backend/src/services/secret-import/secret-import-service.ts +++ b/backend/src/services/secret-import/secret-import-service.ts @@ -808,7 +808,7 @@ export const secretImportServiceFactory = ({ actorOrgId, secrets }: TGetSecretImportsDTO & { - secrets: { secretKey: string; secretValue: string }[] | undefined; + secrets: { secretKey: string; secretValue: string; id: string }[] | undefined; }) => { const { permission } = await permissionService.getProjectPermission({ actor, @@ -877,7 +877,8 @@ export const secretImportServiceFactory = ({ ) .map((otherSecret) => ({ secretId: secret.secretKey, - referencedSecretKey: otherSecret.secretKey + referencedSecretKey: otherSecret.secretKey, + referencedSecretEnv: environment })); }) || []; if (locallyReferenced.length > 0) { diff --git a/backend/src/services/secret-import/secret-import-types.ts b/backend/src/services/secret-import/secret-import-types.ts index e4490e715..41ddbc9e2 100644 --- a/backend/src/services/secret-import/secret-import-types.ts +++ b/backend/src/services/secret-import/secret-import-types.ts @@ -56,11 +56,12 @@ export type FolderResult = { export type SecretResult = { secretId: string; referencedSecretKey: string; + referencedSecretEnv: string; } & FolderResult; export type FolderInfo = { folderName: string; - secrets?: { secretId: string; referencedSecretKey: string }[]; + secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[]; folderId: string; folderImported: boolean; envSlug?: string; diff --git a/backend/src/services/secret-sync/secret-sync-service.ts b/backend/src/services/secret-sync/secret-sync-service.ts index 14a1a1cf0..db350f785 100644 --- a/backend/src/services/secret-sync/secret-sync-service.ts +++ b/backend/src/services/secret-sync/secret-sync-service.ts @@ -23,6 +23,7 @@ import { TDeleteSecretSyncDTO, TFindSecretSyncByIdDTO, TFindSecretSyncByNameDTO, + TListSecretSyncsByFolderId, TListSecretSyncsByProjectId, TSecretSync, TTriggerSecretSyncImportSecretsByIdDTO, @@ -31,12 +32,14 @@ import { TUpdateSecretSyncDTO } from "@app/services/secret-sync/secret-sync-types"; +import { TSecretImportDALFactory } from "../secret-import/secret-import-dal"; import { TSecretSyncDALFactory } from "./secret-sync-dal"; import { SECRET_SYNC_CONNECTION_MAP, SECRET_SYNC_NAME_MAP } from "./secret-sync-maps"; import { TSecretSyncQueueFactory } from "./secret-sync-queue"; type TSecretSyncServiceFactoryDep = { secretSyncDAL: TSecretSyncDALFactory; + secretImportDAL: TSecretImportDALFactory; appConnectionService: Pick; permissionService: Pick; projectBotService: Pick; @@ -53,6 +56,7 @@ export type TSecretSyncServiceFactory = ReturnType { + const { permission } = await permissionService.getProjectPermission({ + actor: actor.type, + actorId: actor.id, + actorAuthMethod: actor.authMethod, + actorOrgId: actor.orgId, + actionProjectType: ActionProjectType.SecretManager, + projectId + }); + + if (permission.cannot(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs)) { + return []; + } + + const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); + if (!folder) return []; + + const folderImports = await secretImportDAL.getFolderImports(secretPath, folder.envId); + + const secretSyncs = await secretSyncDAL.find({ + $in: { + folderId: folderImports.map((folderImport) => folderImport.folderId).concat(folder.id) + } + }); + + return secretSyncs as TSecretSync[]; + }; + const findSecretSyncById = async ({ destination, syncId }: TFindSecretSyncByIdDTO, actor: OrgServiceActor) => { const secretSync = await secretSyncDAL.findById(syncId); @@ -518,6 +553,7 @@ export const secretSyncServiceFactory = ({ return { listSecretSyncOptions, listSecretSyncsByProjectId, + listSecretSyncsBySecretPath, findSecretSyncById, findSecretSyncByName, createSecretSync, diff --git a/backend/src/services/secret-sync/secret-sync-types.ts b/backend/src/services/secret-sync/secret-sync-types.ts index 716c9b44f..e99b31c20 100644 --- a/backend/src/services/secret-sync/secret-sync-types.ts +++ b/backend/src/services/secret-sync/secret-sync-types.ts @@ -144,6 +144,13 @@ export type TListSecretSyncsByProjectId = { destination?: SecretSync; }; +export type TListSecretSyncsByFolderId = { + projectId: string; + secretPath: string; + environment: string; + destination?: SecretSync; +}; + export type TFindSecretSyncByIdDTO = { syncId: string; destination: SecretSync; diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts index 3fa2ccc07..6ab348520 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts @@ -64,7 +64,8 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => { const findOne = async (filter: Partial, tx?: Knex) => { try { const docs = await (tx || db)(TableName.SecretV2) - .where(filter) + // eslint-disable-next-line @typescript-eslint/no-misused-promises + .where(buildFindFilter(filter, TableName.SecretV2)) .leftJoin( TableName.SecretV2JnTag, `${TableName.SecretV2}.id`, diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts index 5c2f6a2f0..6fdcadeff 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts @@ -2,7 +2,7 @@ import path from "node:path"; import RE2 from "re2"; -import { TableName, TSecretFolders, TSecretsV2 } from "@app/db/schemas"; +import { SecretType, TableName, TSecretFolders, TSecretsV2 } from "@app/db/schemas"; import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { groupBy } from "@app/lib/fn"; import { logger } from "@app/lib/logger"; @@ -720,7 +720,7 @@ export const reshapeBridgeSecret = ( secretReminderRecipients: secret.secretReminderRecipients || [], ...(secretValueHidden ? { - secretValue: INFISICAL_SECRET_VALUE_HIDDEN_MASK, + secretValue: secret.type === SecretType.Personal ? secret.value : INFISICAL_SECRET_VALUE_HIDDEN_MASK, secretValueHidden: true } : { diff --git a/docs/documentation/platform/access-controls/assume-privilege.mdx b/docs/documentation/platform/access-controls/assume-privilege.mdx new file mode 100644 index 000000000..a38fd65f0 --- /dev/null +++ b/docs/documentation/platform/access-controls/assume-privilege.mdx @@ -0,0 +1,40 @@ +--- +title: "Assume Privileges" +description: "Learn how to temporarily assume the privileges of a user or machine identity within a project." +--- + +This feature allows authorized users to temporarily take on the permissions of another user or identity. It helps administrators and access managers test and verify permissions before granting access, ensuring everything is set up correctly. +It also reduces back-and-forth with end users when troubleshooting permission-related issues. + +## How It Works + +When an authorized user activates assume privileges mode, they temporarily inherit the target user or identity’s permissions for up to one hour. +During this time, they can perform actions within the system with the same level of access as the target user. + +- **Permission-based**: Only permissions are inherited, not the full identity +- **Time-limited**: Access automatically expires after one hour +- **Audited**: All actions are logged under the original user's account. This means any action taken during the session will be recorded under the entity assuming the privileges, not the target entity. +- **Authorization required**: Only users with the specific **assume privilege** permission can use this feature +- **Scoped to a single project**: You can only assume privileges for one project at a time + +## How to Assume Privileges + + + + Click on the user or identity you want to assume. + + ![Access control page](/images/platform/access-controls/assume-privileges/access-control.png) + + + + Click **Assume Privilege**, then type `assume` to confirm and start your session. + + ![Access control detail page](/images/platform/access-controls/assume-privileges/access-control-detail.png) + + + + You will see a yellow banner indicating that your assume privilege session is active. You can exit at any time by clicking **Exit**. + + ![session start](/images/platform/access-controls/assume-privileges/session-start.png) + + \ No newline at end of file diff --git a/docs/documentation/platform/kms-configuration/aws-kms.mdx b/docs/documentation/platform/kms-configuration/aws-kms.mdx index 3fc5404ae..b4631b1c3 100644 --- a/docs/documentation/platform/kms-configuration/aws-kms.mdx +++ b/docs/documentation/platform/kms-configuration/aws-kms.mdx @@ -9,6 +9,9 @@ This guide will walk you through the steps needed to configure external KMS supp ## Prerequisites +- An AWS KMS Key configured as a `Symmetric` key and with `Encrypt and Decrypt` key usage. + ![Create AWS KMS Key](/images/platform/kms/aws/aws-kms-key-create.png) + Before you begin, you'll first need to choose a method of authentication with AWS from below. diff --git a/docs/documentation/platform/kms/hsm-integration.mdx b/docs/documentation/platform/kms/hsm-integration.mdx index 633377b3d..a9ab2c832 100644 --- a/docs/documentation/platform/kms/hsm-integration.mdx +++ b/docs/documentation/platform/kms/hsm-integration.mdx @@ -268,11 +268,11 @@ For organizations that work with US government agencies, FIPS compliance is almo - When using Kubernetes, you need to mount the path containing the HSM client files. This section covers how to configure your Infisical instance to use an HSM with Kubernetes. + When using Kubernetes, you need to mount the path containing the HSM client files. This section covers how to configure your Infisical instance to use an HSM with Kubernetes. In this example, we are going to be using `/etc/luna-docker`. ```bash - mkdir /etc/hsm-client + mkdir /etc/luna-docker ``` After [setting up your Luna Cloud HSM client](https://thalesdocs.com/gphsm/luna/7/docs/network/Content/install/client_install/add_dpod.htm), you should have a set of files, referred to as the HSM client. You don't need all the files, but for simplicity we recommend copying all the files from the client. @@ -306,20 +306,60 @@ For organizations that work with US government agencies, FIPS compliance is almo The most important parts of the client folder is the `Chrystoki.conf` file, and the `libs`, `plugins`, and `jsp` folders. You need to copy these files to the folder you created in the first step. ```bash - cp -r / /etc/hsm-client + cp -r //* /etc/luna-docker ``` + + + The `/*` wildcard will copy all files and folders within the HSM client. The wildcard is important to ensure that the file structure is inline with the rest of this guide. + + + After copying the files, the `/etc/luna-docker` directory should have the following file structure: + ```bash + $ ls -R /etc/luna-docker + Chrystoki.conf etc lock server-certificate.pem + Chrystoki.conf.tmp2E jsp partition-ca-certificate.pem setenv + lch-support-linux-64bit partition-certificate.pem + bin libs plugins + + /etc/luna-docker/bin: + 64 + + /etc/luna-docker/bin/64: + ckdemo cmu lunacm multitoken vtl + + /etc/luna-docker/etc: + openssl.cnf + + /etc/luna-docker/jsp: + 64 LunaProvider.jar + + /etc/luna-docker/jsp/64: + libLunaAPI.so + + /etc/luna-docker/libs: + 64 + + /etc/luna-docker/libs/64: + libCryptoki2.so + + /etc/luna-docker/lock: + + /etc/luna-docker/plugins: + libcloud.plugin + ``` + The `Chrystoki.conf` file is used to configure the HSM client. You need to update the `Chrystoki.conf` file to point to the correct file paths. - In this example, we will be mounting the `/etc/hsm-client` folder from the host to containers in our deployment's pods at the path `/hsm-client`. This means the contents of `/etc/hsm-client` on the host will be accessible at `/hsm-client` within the containers. + In this example, we will be mounting the `/etc/luna-docker` folder from the host to containers in our deployment's pods at the path `/usr/safenet/lunaclient`. This means the contents of `/etc/luna-docker` on the host will be accessible at `/usr/safenet/lunaclient` within the containers. An example config file will look like this: ```Chrystoki.conf Chrystoki2 = { - # This path points to the mounted path, /hsm-client - LibUNIX64 = /hsm-client/libs/64/libCryptoki2.so; + # This path points to the mounted path, /usr/safenet/lunaclient + LibUNIX64 = /usr/safenet/lunaclient/libs/64/libCryptoki2.so; } Luna = { @@ -339,8 +379,8 @@ For organizations that work with US government agencies, FIPS compliance is almo Misc = { # Update the paths to point to the mounted path if your folder structure is different from the one mentioned in the previous step. - PluginModuleDir = /hsm-client/plugins; - MutexFolder = /hsm-client/lock; + PluginModuleDir = /usr/safenet/lunaclient/plugins; + MutexFolder = /usr/safenet/lunaclient/lock; PE1746Enabled = 1; ToolsDir = /usr/bin; @@ -353,7 +393,7 @@ For organizations that work with US government agencies, FIPS compliance is almo LunaSA Client = { ReceiveTimeout = 20000; # Update the paths to point to the mounted path if your folder structure is different from the one mentioned in the previous step. - SSLConfigFile = /hsm-client/etc/openssl.cnf; + SSLConfigFile = /usr/safenet/lunaclient/etc/openssl.cnf; ClientPrivKeyFile = ./etc/ClientNameKey.pem; ClientCertFile = ./etc/ClientNameCert.pem; ServerCAFile = ./etc/CAFile.pem; @@ -441,7 +481,7 @@ For organizations that work with US government agencies, FIPS compliance is almo ```bash kubectl exec hsm-setup-pod -- mkdir -p /data/ # Create the data directory - kubectl cp ./hsm-client/ hsm-setup-pod:/data/ # Copy the HSM client files into the PVC + kubectl cp /etc/luna-docker/. hsm-setup-pod:/data/ # Copy the HSM client files into the PVC kubectl exec hsm-setup-pod -- chmod -R 755 /data/ # Set the correct permissions for the HSM client files ``` @@ -456,7 +496,7 @@ For organizations that work with US government agencies, FIPS compliance is almo Next we need to update the environment variables used for the deployment. If you followed the [setup instructions for Kubernetes deployments](/self-hosting/deployment-options/kubernetes-helm), you should have a Kubernetes secret called `infisical-secrets`. We need to update the secret with the following environment variables: - - `HSM_LIB_PATH` - The path to the HSM client library _(mapped to `/hsm-client/libs/64/libCryptoki2.so`)_ + - `HSM_LIB_PATH` - The path to the HSM client library _(mapped to `/usr/safenet/lunaclient/libs/64/libCryptoki2.so`)_ - `HSM_PIN` - The PIN for the HSM device that you created when setting up your Luna Cloud HSM client - `HSM_SLOT` - The slot number for the HSM device that you selected when setting up your Luna Cloud HSM client - `HSM_KEY_LABEL` - The label for the HSM key. If no key is found with the provided key label, the HSM will create a new key with the provided label. @@ -471,7 +511,7 @@ For organizations that work with US government agencies, FIPS compliance is almo type: Opaque stringData: # ... Other environment variables ... - HSM_LIB_PATH: "/hsm-client/libs/64/libCryptoki2.so" # If you followed this guide, this will be the path of the Luna Cloud HSM client + HSM_LIB_PATH: "/usr/safenet/lunaclient/libs/64/libCryptoki2.so" # If you followed this guide, this will be the path of the Luna Cloud HSM client HSM_PIN: "" HSM_SLOT: "" HSM_KEY_LABEL: "" @@ -487,7 +527,7 @@ For organizations that work with US government agencies, FIPS compliance is almo After we've successfully configured the PVC and updated our environment variables, we are ready to update the deployment configuration so that the pods it creates can access the HSM client files. - We need to update the Docker image of the deployment to use `infisical/infisical-fips`. The `infisical/infisical-fips` image is a functionally identical image to the `infisical/infisical` image, but it is built with support for HSM encryption. + We need to update the Docker image of the deployment to use `infisical/infisical-fips`. The `infisical/infisical-fips` image is a functionally identical image to the `infisical/infisical` image, but it is built with HSM support. ```yaml # ... The rest of the values.yaml file ... @@ -499,8 +539,7 @@ For organizations that work with US government agencies, FIPS compliance is almo extraVolumeMounts: - name: hsm-data - mountPath: /hsm-client # The path we will mount the HSM client files to - subPath: ./hsm-client + mountPath: /usr/safenet/lunaclient # The path we will mount the HSM client files to extraVolumes: - name: hsm-data diff --git a/docs/images/platform/access-controls/assume-privileges/access-control-detail.png b/docs/images/platform/access-controls/assume-privileges/access-control-detail.png new file mode 100644 index 000000000..e0844b8f4 Binary files /dev/null and b/docs/images/platform/access-controls/assume-privileges/access-control-detail.png differ diff --git a/docs/images/platform/access-controls/assume-privileges/access-control.png b/docs/images/platform/access-controls/assume-privileges/access-control.png new file mode 100644 index 000000000..aa6974cdd Binary files /dev/null and b/docs/images/platform/access-controls/assume-privileges/access-control.png differ diff --git a/docs/images/platform/access-controls/assume-privileges/session-start.png b/docs/images/platform/access-controls/assume-privileges/session-start.png new file mode 100644 index 000000000..1aab112c4 Binary files /dev/null and b/docs/images/platform/access-controls/assume-privileges/session-start.png differ diff --git a/docs/images/platform/kms/aws/aws-kms-key-create.png b/docs/images/platform/kms/aws/aws-kms-key-create.png new file mode 100644 index 000000000..7d8466538 Binary files /dev/null and b/docs/images/platform/kms/aws/aws-kms-key-create.png differ diff --git a/docs/integrations/secret-syncs/teamcity.mdx b/docs/integrations/secret-syncs/teamcity.mdx index e79fc0f0c..af4c8d76a 100644 --- a/docs/integrations/secret-syncs/teamcity.mdx +++ b/docs/integrations/secret-syncs/teamcity.mdx @@ -34,7 +34,7 @@ description: "Learn how to configure a TeamCity Sync for Infisical." - **Build Configuration**: The build configuration to sync secrets to. - Not including a Build Configuration will sync secrets to the entire project. + Not including a Build Configuration will sync secrets to the project. 5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**. @@ -44,6 +44,11 @@ description: "Learn how to configure a TeamCity Sync for Infisical." - **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical. - **Import Secrets (Prioritize Infisical)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Infisical over TeamCity when keys conflict. - **Import Secrets (Prioritize TeamCity)**: Imports secrets from the destination endpoint before syncing, prioritizing values from TeamCity over Infisical when keys conflict. + + + Infisical only syncs secrets from within the target scope; inherited secrets will not be imported. + + - **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only. - **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical. diff --git a/docs/mint.json b/docs/mint.json index 69470e2b7..63eb41ddb 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -160,6 +160,7 @@ }, "documentation/platform/access-controls/additional-privileges", "documentation/platform/access-controls/temporary-access", + "documentation/platform/access-controls/assume-privilege", "documentation/platform/access-controls/access-requests", "documentation/platform/access-controls/project-access-requests", "documentation/platform/pr-workflows", @@ -887,8 +888,8 @@ ] }, { - "group": "LDAP Password", - "pages": [ + "group": "LDAP Password", + "pages": [ "api-reference/endpoints/secret-rotations/ldap-password/create", "api-reference/endpoints/secret-rotations/ldap-password/delete", "api-reference/endpoints/secret-rotations/ldap-password/get-by-id", diff --git a/frontend/src/components/v2/DeleteActionModal/DeleteActionModal.tsx b/frontend/src/components/v2/DeleteActionModal/DeleteActionModal.tsx index a2b69eaba..2fdb56c8c 100644 --- a/frontend/src/components/v2/DeleteActionModal/DeleteActionModal.tsx +++ b/frontend/src/components/v2/DeleteActionModal/DeleteActionModal.tsx @@ -19,6 +19,7 @@ type Props = { formContent?: ReactNode; children?: ReactNode; deletionMessage?: ReactNode; + buttonColorSchema?: "danger" | "primary" | "secondary" | "gray" | null; }; export const DeleteActionModal = ({ @@ -32,6 +33,7 @@ export const DeleteActionModal = ({ buttonText = "Delete", formContent, deletionMessage, + buttonColorSchema = "danger", children }: Props): JSX.Element => { const [inputData, setInputData] = useState(""); @@ -67,7 +69,7 @@ export const DeleteActionModal = ({