requested changes and docs
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -33,7 +33,7 @@ export type TImportEnvKeyDataDTO = {
|
||||
|
||||
export type TImportVaultDataDTO = {
|
||||
vaultAccessToken: string;
|
||||
vaultNamespace: string;
|
||||
vaultNamespace?: string;
|
||||
mappingType: VaultMappingType;
|
||||
vaultUrl: string;
|
||||
} & Omit<TOrgPermission, "orgId">;
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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.
|
||||

|
||||
</Step>
|
||||
<Step>
|
||||
Go to Import/Export on the top right corner, Click on Export Org and save the exported file.
|
||||

|
||||
</Step>
|
||||
<Step>
|
||||
Click on copy to copy the encryption key and save it.
|
||||

|
||||
</Step>
|
||||
<Step>
|
||||
Open the Infisical dashboard and go to Organization Settings > Import.
|
||||

|
||||
</Step>
|
||||
<Step>
|
||||
Upload the exported file from EnvKey, paste the encryption key and click Import.
|
||||

|
||||
</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.
|
||||
44
docs/documentation/platform/external-migrations/envkey.mdx
Normal 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">
|
||||

|
||||
</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.
|
||||

|
||||
</Step>
|
||||
<Step title="Obtain EnvKey encryption key">
|
||||
Click on copy to copy the encryption key and save it.
|
||||

|
||||
</Step>
|
||||
<Step title="Navigate to Infisical external migrations">
|
||||
Open the Infisical dashboard and go to Organization Settings > External Migrations.
|
||||

|
||||
</Step>
|
||||
|
||||
<Step title="Select the EnvKey platform">
|
||||
Select the EnvKey platform and click on Next.
|
||||

|
||||
</Step>
|
||||
|
||||
<Step title="Upload the exported file from EnvKey">
|
||||
Upload the exported file from EnvKey, paste the encryption key and click Import data.
|
||||

|
||||
</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.
|
||||
16
docs/documentation/platform/external-migrations/overview.mdx
Normal 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).
|
||||
127
docs/documentation/platform/external-migrations/vault.mdx
Normal 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.
|
||||
|
||||

|
||||
</Step>
|
||||
|
||||
<Step title="Select the Vault platform">
|
||||
Select the Vault platform and click on Next.
|
||||
|
||||

|
||||
</Step>
|
||||
|
||||
<Step title="Configure the Vault migration">
|
||||
Enter the Vault access token that you generated in the previous step and click Import data.
|
||||
|
||||

|
||||
|
||||
- `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>
|
||||
|
Before Width: | Height: | Size: 896 KiB |
|
Before Width: | Height: | Size: 609 KiB |
|
Before Width: | Height: | Size: 403 KiB After Width: | Height: | Size: 403 KiB |
|
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 413 KiB After Width: | Height: | Size: 413 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 161 KiB |
@@ -49,7 +49,7 @@ export const useImportVault = () => {
|
||||
mappingType
|
||||
}: {
|
||||
vaultAccessToken: string;
|
||||
vaultNamespace: string;
|
||||
vaultNamespace?: string;
|
||||
vaultUrl: string;
|
||||
mappingType: string;
|
||||
}) => {
|
||||
|
||||
@@ -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);
|
||||
@@ -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)}
|
||||
>
|
||||
@@ -0,0 +1 @@
|
||||
export { ExternalMigrationsTab } from "./ExternalMigrationsTab";
|
||||
@@ -1 +0,0 @@
|
||||
export { ImportTab } from "./ImportTab";
|
||||
@@ -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",
|
||||
|
||||