requested changes and docs

This commit is contained in:
Daniel Hougaard
2025-07-24 19:09:24 +04:00
parent 716f061c01
commit 716b88fa49
27 changed files with 274 additions and 81 deletions

View File

@@ -64,7 +64,7 @@ export const registerExternalMigrationRouter = async (server: FastifyZodProvider
schema: {
body: z.object({
vaultAccessToken: z.string(),
vaultNamespace: z.string(),
vaultNamespace: z.string().trim().optional(),
vaultUrl: z.string(),
mappingType: z.nativeEnum(VaultMappingType)
})

View File

@@ -1,6 +1,10 @@
import axios, { AxiosInstance } from "axios";
import { v4 as uuidv4 } from "uuid";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
import { InfisicalImportData, VaultMappingType } from "../external-migration-types";
type VaultData = {
@@ -12,18 +16,25 @@ type VaultData = {
const vaultFactory = () => {
const getMounts = async (request: AxiosInstance) => {
const response = await request.get<
Record<
string,
{
accessor: string;
options: {
version?: string;
} | null;
type: string;
const response = await request
.get<
Record<
string,
{
accessor: string;
options: {
version?: string;
} | null;
type: string;
}
>
>("/v1/sys/mounts")
.catch((err) => {
if (axios.isAxiosError(err)) {
logger.error(err.response?.data, "External migration: Failed to get Vault mounts");
}
>
>("/v1/sys/mounts");
throw err;
});
return response.data;
};
@@ -42,8 +53,11 @@ const vaultFactory = () => {
return response.data.data.keys;
} catch (err) {
if (axios.isAxiosError(err) && err.response?.status === 404) {
return null;
if (axios.isAxiosError(err)) {
logger.error(err.response?.data, "External migration: Failed to get Vault paths");
if (err.response?.status === 404) {
return null;
}
}
throw err;
}
@@ -54,17 +68,24 @@ const vaultFactory = () => {
{ mountPath, secretPath }: { mountPath: string; secretPath: string }
) => {
// For KV v2: /v1/{mount}/data/{path}
const response = await request.get<{
data: {
data: Record<string, string>; // KV v2 has nested data structure
metadata: {
created_time: string;
deletion_time: string;
destroyed: boolean;
version: number;
const response = await request
.get<{
data: {
data: Record<string, string>; // KV v2 has nested data structure
metadata: {
created_time: string;
deletion_time: string;
destroyed: boolean;
version: number;
};
};
};
}>(`/v1/${mountPath}/data/${secretPath}`);
}>(`/v1/${mountPath}/data/${secretPath}`)
.catch((err) => {
if (axios.isAxiosError(err)) {
logger.error(err.response?.data, "External migration: Failed to get Vault secret");
}
throw err;
});
return response.data.data.data;
};
@@ -110,14 +131,14 @@ const vaultFactory = () => {
accessToken
}: {
baseUrl: string;
namespace: string;
namespace?: string;
accessToken: string;
}): Promise<VaultData[]> {
const request = axios.create({
baseURL: baseUrl,
headers: {
"X-Vault-Namespace": namespace,
"X-Vault-Token": accessToken
"X-Vault-Token": accessToken,
...(namespace ? { "X-Vault-Namespace": namespace } : {})
}
});
@@ -152,7 +173,7 @@ const vaultFactory = () => {
});
allData.push({
namespace,
namespace: namespace || "",
mount: mountPath.replace(/\/$/, ""),
path: secretPath.replace(`${cleanMountPath}/`, ""),
secretData
@@ -190,7 +211,7 @@ export const transformToInfisicalFormatNamespaceToProjects = (
for (const data of vaultData) {
const { namespace, mount, path, secretData } = data;
if (mappingType === "namespace") {
if (mappingType === VaultMappingType.Namespace) {
// create project (namespace)
if (!projectMap.has(namespace)) {
const projectId = uuidv4();
@@ -214,7 +235,7 @@ export const transformToInfisicalFormatNamespaceToProjects = (
});
}
environmentId = environmentMap.get(envKey)!;
} else if (mappingType === "key-vault") {
} else if (mappingType === VaultMappingType.KeyVault) {
if (!projectMap.has(mount)) {
const projectId = uuidv4();
projectMap.set(mount, projectId);
@@ -294,10 +315,18 @@ export const importVaultDataFn = async ({
mappingType
}: {
vaultAccessToken: string;
vaultNamespace: string;
vaultNamespace?: string;
vaultUrl: string;
mappingType: VaultMappingType;
}) => {
await blockLocalAndPrivateIpAddresses(vaultUrl);
if (mappingType === VaultMappingType.Namespace && !vaultNamespace) {
throw new BadRequestError({
message: "Vault namespace is required when project mapping type is set to namespace."
});
}
const vaultApi = vaultFactory();
const vaultData = await vaultApi.collectVaultData({

View File

@@ -33,7 +33,7 @@ export type TImportEnvKeyDataDTO = {
export type TImportVaultDataDTO = {
vaultAccessToken: string;
vaultNamespace: string;
vaultNamespace?: string;
mappingType: VaultMappingType;
vaultUrl: string;
} & Omit<TOrgPermission, "orgId">;

View File

@@ -198,6 +198,21 @@
"documentation/platform/workflow-integrations/microsoft-teams-integration"
]
},
{
"group": "External Migrations",
"pages": [
"documentation/platform/external-migrations/overview",
"documentation/platform/external-migrations/envkey",
"documentation/platform/external-migrations/vault"
]
},
{
"group": "External Migrations",
"pages": [
"documentation/platform/workflow-integrations/slack-integration",
"documentation/platform/workflow-integrations/microsoft-teams-integration"
]
},
{
"group": "Admin Consoles",
"pages": [

View File

@@ -1,41 +0,0 @@
---
title: "Migrating from EnvKey to Infisical"
sidebarTitle: "Migration"
description: "Learn how to migrate from EnvKey to Infisical in the easiest way possible."
---
## What is Infisical?
[Infisical](https://infisical.com) is an open-source all-in-one secret management platform that helps developers manage secrets (e.g., API-keys, DB access tokens, [certificates](https://infisical.com/docs/documentation/platform/pki/overview)) across their infrastructure. In addition, Infisical provides [secret sharing](https://infisical.com/docs/documentation/platform/secret-sharing) functionality, ability to [prevent secret leaks](https://infisical.com/docs/cli/scanning-overview), and more.
Infisical is used by 10,000+ organizations across all industries including First American Financial Corporation, Delivery Hero, and [Hugging Face](https://infisical.com/customers/hugging-face).
## Migrating from EnvKey
<Steps>
<Step>
Open the EnvKey dashboard and go to My Org.
![EnvKey Dashboard](../../images/guides/import-envkey/envkey-dashboard.png)
</Step>
<Step>
Go to Import/Export on the top right corner, Click on Export Org and save the exported file.
![Export organization](../../images/guides/import-envkey/envkey-export.png)
</Step>
<Step>
Click on copy to copy the encryption key and save it.
![Copy encryption key](../../images/guides/import-envkey/copy-encryption-key.png)
</Step>
<Step>
Open the Infisical dashboard and go to Organization Settings > Import.
![Infisical Organization settings](../../images/guides/import-envkey/infisical-import-dashboard.png)
</Step>
<Step>
Upload the exported file from EnvKey, paste the encryption key and click Import.
![Infisical Import EnvKey](../../images/guides/import-envkey/infisical-import-envkey.png)
</Step>
</Steps>
## Talk to our team
To make the migration process even more seamless, you can [schedule a meeting with our team](https://infisical.cal.com/vlad/migration-from-envkey-to-infisical) to learn more about how Infisical compares to EnvKey and discuss unique needs of your organization. You are also welcome to email us at [support@infisical.com](mailto:support@infisical.com) to ask any questions or get any technical help.

View File

@@ -0,0 +1,44 @@
---
title: "Migrating from EnvKey to Infisical"
sidebarTitle: "EnvKey"
description: "Learn how to migrate secrets from EnvKey to Infisical."
---
## Migrating from EnvKey
<Steps>
<Step title="Open the EnvKey dashboard and go to My Org">
![EnvKey Dashboard](/images/platform/external-migrations/envkey-dashboard.png)
</Step>
<Step title="Export your EnvKey organization">
Go to Import/Export on the top right corner, Click on Export Org and save the exported file.
![Export organization](/images/platform/external-migrations/envkey-export.png)
</Step>
<Step title="Obtain EnvKey encryption key">
Click on copy to copy the encryption key and save it.
![Copy encryption key](/images/platform/external-migrations/envkey-copy-encryption-key.png)
</Step>
<Step title="Navigate to Infisical external migrations">
Open the Infisical dashboard and go to Organization Settings > External Migrations.
![Infisical Organization settings](/images/platform/external-migrations/infisical-external-migration-dashboard.png)
</Step>
<Step title="Select the EnvKey platform">
Select the EnvKey platform and click on Next.
![Select EnvKey platform](/images/platform/external-migrations/infisical-import-envkey-modal.png)
</Step>
<Step title="Upload the exported file from EnvKey">
Upload the exported file from EnvKey, paste the encryption key and click Import data.
![Infisical Import EnvKey](/images/platform/external-migrations/infisical-import-envkey.png)
</Step>
</Steps>
<Note>
It may take several minutes to complete the migration. You will receive an email when the migration is complete, or if there were any errors during the migration process.
</Note>
## Talk to our team
To make the migration process even more seamless, you can [schedule a meeting with our team](https://infisical.cal.com/vlad/migration-from-envkey-to-infisical) to learn more about how Infisical compares to EnvKey and discuss unique needs of your organization. You are also welcome to email us at [support@infisical.com](mailto:support@infisical.com) to ask any questions or get any technical help.

View File

@@ -0,0 +1,16 @@
---
title: "External Migrations"
sidebarTitle: "Overview"
description: "Learn how to migrate secrets from third-party secrets management platforms to Infisical."
---
## Overview
Infisical supports migrating secrets from third-party secrets management platforms to Infisical. This is useful if you're looking to easily switch to Infisical and wish to move over your existing secrets from a different platform.
## Supported Platforms
- [EnvKey](./envkey)
- [Vault](./vault)
We're always looking to add more migration paths for other providers. If we're missing a platform, please open an issue on our [GitHub repository](https://github.com/infisical/infisical/issues).

View File

@@ -0,0 +1,127 @@
---
title: "Migrating from Vault to Infisical"
sidebarTitle: "Vault"
description: "Learn how to migrate secrets from Vault to Infisical."
---
## Migrating from Vault
Migrating from Vault Self-Hosted or Dedicated Vault is a straight forward process with our inbuilt migration option. In order to migrate from Vault, you'll need to provide Infisical an access token to your Vault instance.
Currently the Vault migration only supports migrating secrets from the KV v2 secrets engine. If you're using a different secrets engine, please open an issue on our [GitHub repository](https://github.com/infisical/infisical/issues).
### Prerequisites
- A Vault instance with the KV v2 secrets engine enabled.
- An access token to your Vault instance.
### Project Mapping
When migrating from Vault, you'll need to choose how you want to map your Vault resources to Infisical projects.
There are two options for project mapping:
- `Namespace`: This will map your selected Vault namespace to a single Infisical project. When you select this option, each KV secret engine within the namespace will be mapped to a single Infisical project. Each KV secret engine will be mapped to a Infisical environment within the project. This means if you have 3 KV secret engines, you'll have 3 environments inside the same project, where the name of the environments correspond to the name of the KV secret engines.
- `Key Vault`: This will map all the KV secret engines within your Vault instance to a Infisical project. Each KV engine will be created as a Infisical project. This means if you have 3 KV secret engines, you'll have 3 Infisical projects. For each of the created projects, a single default environment will be created called `Production`, which will contain all your secrets from the corresponding KV secret engine.
<Steps>
<Step title="Create a Vault policy">
In order to migrate from Vault, you'll need to create a Vault policy that allows Infisical to read the secrets and metadata from the KV v2 secrets engines within your Vault instance.
```python
# Allow listing secret engines/mounts
path "sys/mounts" {
capabilities = ["read", "list"]
}
# For KV v2 engines - access to both data and metadata
path "*/data/*" {
capabilities = ["read", "list"]
}
path "*/metadata/*" {
capabilities = ["read", "list"]
}
# If using Vault Enterprise - allow listing namespaces
path "sys/namespaces" {
capabilities = ["list", "read"]
}
# Cross-namespace access (Enterprise only)
path "+/*" {
capabilities = ["read", "list"]
}
path "+/sys/mounts" {
capabilities = ["read", "list"]
}
```
Save this policy with the name `infisical-migration`.
</Step>
<Step title="Generate an access token">
You can use the Vault CLI to easily generate an access token for the new `infisical-migration` policy that you created in the previous step.
```bash
vault token create --policy="infisical-migration"
```
After generating the token, you should see the following output:
```t
$ vault token create --policy="infisical-migration"
Key Value
--- -----
token <your-access-token>
token_accessor p6kJDiBSzYYdabJUIpGCsCBm
token_duration 768h
token_renewable true
token_policies ["default" "infisical-migration"]
identity_policies []
policies ["default" "infisical-migration"]
```
Copy the `token` field and save it for later, as you'll need this when configuring the migration to Infisical.
</Step>
<Step title="Navigate to Infisical external migrations">
Open the Infisical dashboard and go to Organization Settings > External Migrations.
![Infisical Organization settings](/images/platform/external-migrations/infisical-external-migration-dashboard.png)
</Step>
<Step title="Select the Vault platform">
Select the Vault platform and click on Next.
![Select Vault platform](/images/platform/external-migrations/infisical-import-vault-modal.png)
</Step>
<Step title="Configure the Vault migration">
Enter the Vault access token that you generated in the previous step and click Import data.
![Configure Vault migration](/images/platform/external-migrations/infisical-import-vault.png)
- `Vault URL`: The URL of your Vault instance.
- `Vault Namespace`: The namespace of your Vault instance. This is optional, and can be left blank if you're not using namespaces for your Vault instance.
- `Vault Access Token`: The access token that you generated in the previous step.
- `Project Mapping`: Choose how you want to map your Vault resources to Infisical projects. You can review the mapping options in the [Project Mapping](#project-mapping) section.
Click on Import data to start the migration.
</Step>
</Steps>
<Note>
It may take several minutes to complete the migration. You will receive an email when the migration is complete, or if there were any errors during the migration process.
</Note>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 609 KiB

View File

Before

Width:  |  Height:  |  Size: 403 KiB

After

Width:  |  Height:  |  Size: 403 KiB

View File

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

View File

Before

Width:  |  Height:  |  Size: 413 KiB

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

View File

@@ -49,7 +49,7 @@ export const useImportVault = () => {
mappingType
}: {
vaultAccessToken: string;
vaultNamespace: string;
vaultNamespace?: string;
vaultUrl: string;
mappingType: string;
}) => {

View File

@@ -9,7 +9,7 @@ import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { SelectImportFromPlatformModal } from "./components/SelectImportFromPlatformModal";
export const ImportTab = () => {
export const ExternalMigrationsTab = () => {
const { membership } = useOrgPermission();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["selectImportPlatform"] as const);

View File

@@ -62,7 +62,7 @@ const MAPPING_TYPE_MENU_ITEMS = [
export const VaultPlatformModal = ({ onClose }: Props) => {
const formSchema = z.object({
vaultUrl: z.string().min(1),
vaultNamespace: z.string().min(1),
vaultNamespace: z.string().trim().optional(),
vaultAccessToken: z.string().min(1),
mappingType: z.nativeEnum(VaultMappingType).default(VaultMappingType.KeyVault)
});
@@ -122,7 +122,7 @@ export const VaultPlatformModal = ({ onClose }: Props) => {
errorText={error?.message}
isError={Boolean(error)}
>
<Input type="password" placeholder="" {...field} />
<Input placeholder="" {...field} />
</FormControl>
)}
/>
@@ -132,7 +132,6 @@ export const VaultPlatformModal = ({ onClose }: Props) => {
render={({ field, fieldState: { error } }) => (
<FormControl
label="Vault Namespace"
isRequired
errorText={error?.message}
isError={Boolean(error)}
>

View File

@@ -0,0 +1 @@
export { ExternalMigrationsTab } from "./ExternalMigrationsTab";

View File

@@ -1 +0,0 @@
export { ImportTab } from "./ImportTab";

View File

@@ -5,7 +5,7 @@ import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { AuditLogStreamsTab } from "../AuditLogStreamTab";
import { ImportTab } from "../ImportTab";
import { ExternalMigrationsTab } from "../ExternalMigrationsTab";
import { KmipTab } from "../KmipTab/OrgKmipTab";
import { OrgEncryptionTab } from "../OrgEncryptionTab";
import { OrgGeneralTab } from "../OrgGeneralTab";
@@ -39,7 +39,11 @@ export const OrgTabGroup = () => {
component: OrgWorkflowIntegrationTab
},
{ name: "Audit Log Streams", key: "tag-audit-log-streams", component: AuditLogStreamsTab },
{ name: "Import", key: "tab-import", component: ImportTab },
{
name: "External Migrations",
key: "tab-external-migrations",
component: ExternalMigrationsTab
},
{
name: "Project Templates",
key: "project-templates",