mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Support for namespaces (for HCP)
This commit is contained in:
@@ -1860,6 +1860,7 @@ export const AppConnections = {
|
||||
},
|
||||
HC_VAULT: {
|
||||
instanceUrl: "The Hashicrop Vault instance URL to connect with.",
|
||||
namespace: "The Hashicrop Vault namespace to connect with.",
|
||||
accessToken: "The access token used to connect with Hashicorp Vault.",
|
||||
roleId: "The Role ID used to connect with Hashicorp Vault.",
|
||||
secretId: "The Secret ID used to connect with Hashicorp Vault."
|
||||
|
||||
@@ -49,7 +49,12 @@ export const getHCVaultAccessToken = async (connection: TValidateHCVaultConnecti
|
||||
const tokenResp = await request.post<TokenRespData>(
|
||||
`${removeTrailingSlash(instanceUrl)}/v1/auth/approle/login`,
|
||||
{ role_id: roleId, secret_id: secretId },
|
||||
{ headers: { "Content-Type": "application/json" } }
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(connection.credentials.namespace ? { "X-Vault-Namespace": connection.credentials.namespace } : {})
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (tokenResp.status !== 200) {
|
||||
@@ -95,7 +100,10 @@ export const listHCVaultMounts = async (appConnection: THCVaultConnection) => {
|
||||
const accessToken = await getHCVaultAccessToken(appConnection);
|
||||
|
||||
const { data } = await request.get<THCVaultMountResponse>(`${instanceUrl}/v1/sys/mounts`, {
|
||||
headers: { "X-Vault-Token": accessToken }
|
||||
headers: {
|
||||
"X-Vault-Token": accessToken,
|
||||
...(appConnection.credentials.namespace ? { "X-Vault-Namespace": appConnection.credentials.namespace } : {})
|
||||
}
|
||||
});
|
||||
|
||||
const mounts: string[] = [];
|
||||
|
||||
@@ -17,8 +17,11 @@ const InstanceUrlSchema = z
|
||||
.url("Invalid Instance URL")
|
||||
.describe(AppConnections.CREDENTIALS.HC_VAULT.instanceUrl);
|
||||
|
||||
const NamespaceSchema = z.string().trim().optional().describe(AppConnections.CREDENTIALS.HC_VAULT.namespace);
|
||||
|
||||
export const HCVaultConnectionAccessTokenCredentialsSchema = z.object({
|
||||
instanceUrl: InstanceUrlSchema,
|
||||
namespace: NamespaceSchema,
|
||||
accessToken: z
|
||||
.string()
|
||||
.trim()
|
||||
@@ -28,6 +31,7 @@ export const HCVaultConnectionAccessTokenCredentialsSchema = z.object({
|
||||
|
||||
export const HCVaultConnectionAppRoleCredentialsSchema = z.object({
|
||||
instanceUrl: InstanceUrlSchema,
|
||||
namespace: NamespaceSchema,
|
||||
roleId: z.string().trim().min(1, "Role ID required").describe(AppConnections.CREDENTIALS.HC_VAULT.roleId),
|
||||
secretId: z.string().trim().min(1, "Secret ID required").describe(AppConnections.CREDENTIALS.HC_VAULT.secretId)
|
||||
});
|
||||
|
||||
@@ -10,12 +10,13 @@ import {
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
const listHCVaultVariables = async ({ instanceUrl, mount, accessToken, path }: THCVaultListVariables) => {
|
||||
const listHCVaultVariables = async ({ instanceUrl, namespace, mount, accessToken, path }: THCVaultListVariables) => {
|
||||
const { data } = await request.get<THCVaultListVariablesResponse>(
|
||||
`${instanceUrl}/v1/${removeTrailingSlash(mount)}/data/${path}`,
|
||||
{
|
||||
headers: {
|
||||
"X-Vault-Token": accessToken
|
||||
"X-Vault-Token": accessToken,
|
||||
...(namespace ? { "X-Vault-Namespace": namespace } : {})
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -24,7 +25,14 @@ const listHCVaultVariables = async ({ instanceUrl, mount, accessToken, path }: T
|
||||
};
|
||||
|
||||
// Hashicorp Vault updates all variables in one batch. This is to respect their versioning
|
||||
const updateHCVaultVariables = async ({ path, instanceUrl, accessToken, mount, data }: TPostHCVaultVariable) =>
|
||||
const updateHCVaultVariables = async ({
|
||||
path,
|
||||
instanceUrl,
|
||||
namespace,
|
||||
accessToken,
|
||||
mount,
|
||||
data
|
||||
}: TPostHCVaultVariable) =>
|
||||
request.post(
|
||||
`${instanceUrl}/v1/${removeTrailingSlash(mount)}/data/${path}`,
|
||||
{
|
||||
@@ -33,6 +41,7 @@ const updateHCVaultVariables = async ({ path, instanceUrl, accessToken, mount, d
|
||||
{
|
||||
headers: {
|
||||
"X-Vault-Token": accessToken,
|
||||
...(namespace ? { "X-Vault-Namespace": namespace } : {}),
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
@@ -46,10 +55,17 @@ export const HCVaultSyncFns = {
|
||||
syncOptions: { disableSecretDeletion }
|
||||
} = secretSync;
|
||||
|
||||
const { namespace } = connection.credentials;
|
||||
const accessToken = await getHCVaultAccessToken(connection);
|
||||
const instanceUrl = await getHCVaultInstanceUrl(connection);
|
||||
|
||||
const variables = await listHCVaultVariables({ instanceUrl, accessToken, mount, path });
|
||||
const variables = await listHCVaultVariables({
|
||||
instanceUrl,
|
||||
accessToken,
|
||||
namespace,
|
||||
mount,
|
||||
path
|
||||
});
|
||||
let tainted = false;
|
||||
|
||||
for (const entry of Object.entries(secretMap)) {
|
||||
@@ -73,7 +89,7 @@ export const HCVaultSyncFns = {
|
||||
if (!tainted) return;
|
||||
|
||||
try {
|
||||
await updateHCVaultVariables({ accessToken, instanceUrl, mount, path, data: variables });
|
||||
await updateHCVaultVariables({ accessToken, instanceUrl, namespace, mount, path, data: variables });
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error
|
||||
@@ -86,10 +102,11 @@ export const HCVaultSyncFns = {
|
||||
destinationConfig: { mount, path }
|
||||
} = secretSync;
|
||||
|
||||
const { namespace } = connection.credentials;
|
||||
const accessToken = await getHCVaultAccessToken(connection);
|
||||
const instanceUrl = await getHCVaultInstanceUrl(connection);
|
||||
|
||||
const variables = await listHCVaultVariables({ instanceUrl, accessToken, mount, path });
|
||||
const variables = await listHCVaultVariables({ instanceUrl, namespace, accessToken, mount, path });
|
||||
|
||||
for await (const [key] of Object.entries(variables)) {
|
||||
if (key in secretMap) {
|
||||
@@ -98,7 +115,7 @@ export const HCVaultSyncFns = {
|
||||
}
|
||||
|
||||
try {
|
||||
await updateHCVaultVariables({ accessToken, instanceUrl, mount, path, data: variables });
|
||||
await updateHCVaultVariables({ accessToken, instanceUrl, namespace, mount, path, data: variables });
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error
|
||||
@@ -111,11 +128,13 @@ export const HCVaultSyncFns = {
|
||||
destinationConfig: { mount, path }
|
||||
} = secretSync;
|
||||
|
||||
const { namespace } = connection.credentials;
|
||||
const accessToken = await getHCVaultAccessToken(connection);
|
||||
const instanceUrl = await getHCVaultInstanceUrl(connection);
|
||||
|
||||
const variables = await listHCVaultVariables({
|
||||
instanceUrl,
|
||||
namespace,
|
||||
accessToken,
|
||||
mount,
|
||||
path
|
||||
|
||||
@@ -25,6 +25,7 @@ export type THCVaultListVariablesResponse = {
|
||||
export type THCVaultListVariables = {
|
||||
accessToken: string;
|
||||
instanceUrl: string;
|
||||
namespace?: string;
|
||||
mount: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 495 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 673 KiB After Width: | Height: | Size: 643 KiB |
@@ -4,7 +4,7 @@ description: "Learn how to configure a Hashicorp Vault Connection for Infisical.
|
||||
---
|
||||
|
||||
<Note>
|
||||
The Hashicorp Vault UI may vary based on whether you're self-hosting or using HCP, but the written directions should still be universal.
|
||||
Infisical is compatible with Vault Self-hosted, HCP Vault Dedicated, and HCP Vault Enterprise deployments. Please note that HCP Generic Secrets are currently not supported.
|
||||
</Note>
|
||||
|
||||
Infisical supports two methods for connecting to Hashicorp Vault.
|
||||
@@ -114,10 +114,14 @@ Infisical supports two methods for connecting to Hashicorp Vault.
|
||||
|
||||
Save this value for later steps.
|
||||
</Tab>
|
||||
<Tab title="HCP">
|
||||
<Tab title="Hashicorp Cloud Platform">
|
||||
On HCP instances, you may need to navigate to **Cluster Overview** to see your cluster URL. Save this value for later steps.
|
||||
|
||||

|
||||
|
||||
<Note>
|
||||
Cluster Overview is found in the HCP dashboard, not in your cluster's web UI.
|
||||
</Note>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -144,6 +148,7 @@ Infisical supports two methods for connecting to Hashicorp Vault.
|
||||
- **Name**: The name of the connection being created. Must be slug-friendly.
|
||||
- **Description**: An optional description to provide details about this connection.
|
||||
- **Instance URL**: The URL of your Hashicorp Vault instance.
|
||||
- **Namespace (optional)**: The namespace within your vault. Self-hosted and enterprise clusters may not use namespaces.
|
||||
- **Access Token**: The Access Token generated in the steps above (if using Access Token authentication method).
|
||||
- **Role ID**: The Role ID generated in the steps above (if using AppRole authentication method).
|
||||
- **Secret ID**: The Secret ID generated in the steps above (if using AppRole authentication method).
|
||||
|
||||
@@ -11,6 +11,7 @@ export type THCVaultConnection = TRootAppConnection & { app: AppConnection.HCVau
|
||||
method: HCVaultConnectionMethod.AccessToken;
|
||||
credentials: {
|
||||
instanceUrl: string;
|
||||
namespace?: string;
|
||||
accessToken: string;
|
||||
};
|
||||
}
|
||||
@@ -18,6 +19,7 @@ export type THCVaultConnection = TRootAppConnection & { app: AppConnection.HCVau
|
||||
method: HCVaultConnectionMethod.AppRole;
|
||||
credentials: {
|
||||
instanceUrl: string;
|
||||
namespace?: string;
|
||||
roleId: string;
|
||||
secretId: string;
|
||||
};
|
||||
|
||||
@@ -35,11 +35,14 @@ const InstanceUrlSchema = z
|
||||
.min(1, "Instance URL required")
|
||||
.url("Invalid Instance URL");
|
||||
|
||||
const NamespaceSchema = z.string().trim().optional();
|
||||
|
||||
const formSchema = z.discriminatedUnion("method", [
|
||||
rootSchema.extend({
|
||||
method: z.literal(HCVaultConnectionMethod.AccessToken),
|
||||
credentials: z.object({
|
||||
instanceUrl: InstanceUrlSchema,
|
||||
namespace: NamespaceSchema,
|
||||
accessToken: z.string().trim().min(1, "Access Token required")
|
||||
})
|
||||
}),
|
||||
@@ -47,6 +50,7 @@ const formSchema = z.discriminatedUnion("method", [
|
||||
method: z.literal(HCVaultConnectionMethod.AppRole),
|
||||
credentials: z.object({
|
||||
instanceUrl: InstanceUrlSchema,
|
||||
namespace: NamespaceSchema,
|
||||
roleId: z.string().trim().min(1, "Role ID required"),
|
||||
secretId: z.string().trim().min(1, "Secret ID required")
|
||||
})
|
||||
@@ -127,6 +131,23 @@ export const HCVaultConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="credentials.namespace"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Namespace"
|
||||
isOptional
|
||||
tooltipClassName="max-w-sm"
|
||||
tooltipText="On self-hosted and enterprise clusters there may not be namespaces."
|
||||
>
|
||||
<Input {...field} placeholder="admin" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{selectedMethod === HCVaultConnectionMethod.AccessToken ? (
|
||||
<Controller
|
||||
name="credentials.accessToken"
|
||||
|
||||
Reference in New Issue
Block a user