mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 18:27:36 +00:00
Add Chef PKI sync
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
|
||||
import { FormControl, Input } from "@app/components/v2";
|
||||
import { PkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
import { TPkiSyncForm } from "./schemas/pki-sync-schema";
|
||||
import { PkiSyncConnectionField } from "./PkiSyncConnectionField";
|
||||
|
||||
export const ChefPkiSyncFields = () => {
|
||||
const { control, setValue } = useFormContext<TPkiSyncForm & { destination: PkiSync.Chef }>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<PkiSyncConnectionField
|
||||
onChange={() => {
|
||||
setValue("destinationConfig.dataBagName", "");
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
name="destinationConfig.dataBagName"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
label="Data Bag Name"
|
||||
tooltipText="Enter your Chef data bag name where certificates will be stored. This data bag will be used to store SSL/TLS certificates, private keys, and certificate chains. Data bag names must contain only alphanumeric characters, underscores, and hyphens."
|
||||
>
|
||||
<Input {...field} placeholder="ssl_certificates" maxLength={255} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -5,6 +5,7 @@ import { PkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
import { TPkiSyncForm } from "./schemas/pki-sync-schema";
|
||||
import { AwsCertificateManagerPkiSyncFields } from "./AwsCertificateManagerPkiSyncFields";
|
||||
import { AzureKeyVaultPkiSyncFields } from "./AzureKeyVaultPkiSyncFields";
|
||||
import { ChefPkiSyncFields } from "./ChefPkiSyncFields";
|
||||
|
||||
export const PkiSyncDestinationFields = () => {
|
||||
const { watch } = useFormContext<TPkiSyncForm>();
|
||||
@@ -16,6 +17,8 @@ export const PkiSyncDestinationFields = () => {
|
||||
return <AzureKeyVaultPkiSyncFields />;
|
||||
case PkiSync.AwsCertificateManager:
|
||||
return <AwsCertificateManagerPkiSyncFields />;
|
||||
case PkiSync.Chef:
|
||||
return <ChefPkiSyncFields />;
|
||||
default:
|
||||
return (
|
||||
<div className="flex items-center justify-center rounded-md border border-red-500 bg-red-100 p-4 text-red-700">
|
||||
|
||||
@@ -183,6 +183,52 @@ export const PkiSyncOptionsFields = ({ destination }: Props) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentDestination === PkiSync.Chef && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="syncOptions.preserveItemOnRenewal"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||
<Switch
|
||||
className="bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
||||
id="preserve-item-on-renewal"
|
||||
thumbClassName="bg-mineshaft-800"
|
||||
onCheckedChange={onChange}
|
||||
isChecked={value}
|
||||
>
|
||||
<p>
|
||||
Preserve Data Bag Item on Renewal{" "}
|
||||
<Tooltip
|
||||
className="max-w-md"
|
||||
content={
|
||||
<>
|
||||
<p>
|
||||
<strong>Only applies to certificate renewals:</strong> When a certificate
|
||||
is renewed in Infisical, this option controls how the renewed certificate
|
||||
is handled in Chef.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
When enabled, the renewed certificate will update the existing data bag
|
||||
item, preserving the same item name. This allows consuming services to
|
||||
continue using the same data bag item without requiring updates to Chef
|
||||
cookbooks or recipes.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
When disabled, the renewed certificate will be created as a new data bag
|
||||
item with a new name, and the old item will be removed.
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
|
||||
</Tooltip>
|
||||
</p>
|
||||
</Switch>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="syncOptions.certificateNameSchema"
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { PkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
import { BasePkiSyncSchema } from "./base-pki-sync-schema";
|
||||
|
||||
const ChefSyncOptionsSchema = z.object({
|
||||
canImportCertificates: z.boolean().default(false),
|
||||
canRemoveCertificates: z.boolean().default(true),
|
||||
preserveItemOnRenewal: z.boolean().default(true),
|
||||
updateExistingCertificates: z.boolean().default(true),
|
||||
certificateNameSchema: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine(
|
||||
(val) => {
|
||||
if (!val) return true;
|
||||
|
||||
const allowedOptionalPlaceholders = [
|
||||
"{{environment}}",
|
||||
"{{profileId}}",
|
||||
"{{commonName}}",
|
||||
"{{friendlyName}}"
|
||||
];
|
||||
|
||||
const allowedPlaceholdersRegexPart = ["{{certificateId}}", ...allowedOptionalPlaceholders]
|
||||
.map((p) => p.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"))
|
||||
.join("|");
|
||||
|
||||
const allowedContentRegex = new RegExp(
|
||||
`^([a-zA-Z0-9_\\-]|${allowedPlaceholdersRegexPart})*$`
|
||||
);
|
||||
const contentIsValid = allowedContentRegex.test(val);
|
||||
|
||||
if (val.trim()) {
|
||||
const certificateIdRegex = /\{\{certificateId\}\}/;
|
||||
const certificateIdIsPresent = certificateIdRegex.test(val);
|
||||
return contentIsValid && certificateIdIsPresent;
|
||||
}
|
||||
|
||||
return contentIsValid;
|
||||
},
|
||||
{
|
||||
message:
|
||||
"Certificate item name schema must include exactly one {{certificateId}} placeholder. It can also include {{environment}}, {{profileId}}, {{commonName}}, or {{friendlyName}} placeholders. Only alphanumeric characters (a-z, A-Z, 0-9), hyphens (-), and underscores (_) are allowed besides the placeholders."
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
export const ChefPkiSyncDestinationSchema = BasePkiSyncSchema(ChefSyncOptionsSchema).merge(
|
||||
z.object({
|
||||
destination: z.literal(PkiSync.Chef),
|
||||
destinationConfig: z.object({
|
||||
dataBagName: z
|
||||
.string()
|
||||
.min(1, "Data bag name is required")
|
||||
.max(255, "Data bag name must be less than 255 characters")
|
||||
.regex(
|
||||
/^[a-zA-Z0-9_-]+$/,
|
||||
"Data bag name can only contain alphanumeric characters, underscores, and hyphens"
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
|
||||
export const UpdateChefPkiSyncDestinationSchema = ChefPkiSyncDestinationSchema.partial().merge(
|
||||
z.object({
|
||||
name: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Name is required")
|
||||
.max(255, "Name must be less than 255 characters"),
|
||||
destination: z.literal(PkiSync.Chef),
|
||||
connection: z.object({
|
||||
id: z.string().uuid("Invalid connection ID format"),
|
||||
name: z
|
||||
.string()
|
||||
.min(1, "Connection name is required")
|
||||
.max(255, "Connection name must be less than 255 characters")
|
||||
})
|
||||
})
|
||||
);
|
||||
@@ -8,15 +8,21 @@ import {
|
||||
AzureKeyVaultPkiSyncDestinationSchema,
|
||||
UpdateAzureKeyVaultPkiSyncDestinationSchema
|
||||
} from "./azure-key-vault-pki-sync-destination-schema";
|
||||
import {
|
||||
ChefPkiSyncDestinationSchema,
|
||||
UpdateChefPkiSyncDestinationSchema
|
||||
} from "./chef-pki-sync-destination-schema";
|
||||
|
||||
const PkiSyncUnionSchema = z.discriminatedUnion("destination", [
|
||||
AzureKeyVaultPkiSyncDestinationSchema,
|
||||
AwsCertificateManagerPkiSyncDestinationSchema
|
||||
AwsCertificateManagerPkiSyncDestinationSchema,
|
||||
ChefPkiSyncDestinationSchema
|
||||
]);
|
||||
|
||||
const UpdatePkiSyncUnionSchema = z.discriminatedUnion("destination", [
|
||||
UpdateAzureKeyVaultPkiSyncDestinationSchema,
|
||||
UpdateAwsCertificateManagerPkiSyncDestinationSchema
|
||||
UpdateAwsCertificateManagerPkiSyncDestinationSchema,
|
||||
UpdateChefPkiSyncDestinationSchema
|
||||
]);
|
||||
|
||||
export const PkiSyncFormSchema = PkiSyncUnionSchema;
|
||||
|
||||
@@ -15,10 +15,15 @@ export const PKI_SYNC_MAP: Record<
|
||||
[PkiSync.AwsCertificateManager]: {
|
||||
name: "AWS Certificate Manager",
|
||||
image: "Amazon Web Services.png"
|
||||
},
|
||||
[PkiSync.Chef]: {
|
||||
name: "Chef",
|
||||
image: "Chef.png"
|
||||
}
|
||||
};
|
||||
|
||||
export const PKI_SYNC_CONNECTION_MAP: Record<PkiSync, AppConnection> = {
|
||||
[PkiSync.AzureKeyVault]: AppConnection.AzureKeyVault,
|
||||
[PkiSync.AwsCertificateManager]: AppConnection.AWS
|
||||
[PkiSync.AwsCertificateManager]: AppConnection.AWS,
|
||||
[PkiSync.Chef]: AppConnection.Chef
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export enum PkiSync {
|
||||
AzureKeyVault = "azure-key-vault",
|
||||
AwsCertificateManager = "aws-certificate-manager"
|
||||
AwsCertificateManager = "aws-certificate-manager",
|
||||
Chef = "chef"
|
||||
}
|
||||
|
||||
export enum PkiSyncStatus {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
|
||||
import { PkiSync } from "../enums";
|
||||
import { TRootPkiSync } from "./common";
|
||||
|
||||
export type TChefPkiSync = TRootPkiSync & {
|
||||
destination: PkiSync.Chef;
|
||||
destinationConfig: {
|
||||
dataBagName: string;
|
||||
};
|
||||
connection: {
|
||||
app: AppConnection.Chef;
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
@@ -7,6 +7,10 @@ export type RootPkiSyncOptions = {
|
||||
canRemoveCertificates: boolean;
|
||||
certificateNamePrefix?: string;
|
||||
certificateNameSchema?: string;
|
||||
preserveArn?: boolean;
|
||||
enableVersioning?: boolean;
|
||||
preserveItemOnRenewal?: boolean;
|
||||
updateExistingCertificates?: boolean;
|
||||
};
|
||||
|
||||
export type TRootPkiSync = {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { PkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
import { TAwsCertificateManagerPkiSync } from "./aws-certificate-manager-sync";
|
||||
import { TAzureKeyVaultPkiSync } from "./azure-key-vault-sync";
|
||||
import { TChefPkiSync } from "./chef-sync";
|
||||
|
||||
export type TPkiSyncOption = {
|
||||
name: string;
|
||||
@@ -16,7 +17,7 @@ export type TPkiSyncOption = {
|
||||
minCertificateNameLength?: number;
|
||||
};
|
||||
|
||||
export type TPkiSync = TAzureKeyVaultPkiSync | TAwsCertificateManagerPkiSync;
|
||||
export type TPkiSync = TAzureKeyVaultPkiSync | TAwsCertificateManagerPkiSync | TChefPkiSync;
|
||||
|
||||
export type TListPkiSyncs = { pkiSyncs: TPkiSync[] };
|
||||
|
||||
@@ -31,6 +32,10 @@ type TCreatePkiSyncDTOBase = {
|
||||
canRemoveCertificates: boolean;
|
||||
certificateNamePrefix?: string;
|
||||
certificateNameSchema?: string;
|
||||
preserveArn?: boolean;
|
||||
enableVersioning?: boolean;
|
||||
preserveItemOnRenewal?: boolean;
|
||||
updateExistingCertificates?: boolean;
|
||||
};
|
||||
isAutoSyncEnabled: boolean;
|
||||
subscriberId?: string | null;
|
||||
@@ -43,6 +48,7 @@ export type TCreatePkiSyncDTO = TCreatePkiSyncDTOBase & {
|
||||
destinationConfig: {
|
||||
vaultBaseUrl?: string;
|
||||
region?: string;
|
||||
dataBagName?: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -77,4 +83,5 @@ export type TTriggerPkiSyncRemoveCertificatesDTO = {
|
||||
|
||||
export * from "./aws-certificate-manager-sync";
|
||||
export * from "./azure-key-vault-sync";
|
||||
export * from "./chef-sync";
|
||||
export * from "./common";
|
||||
|
||||
+5
-1
@@ -13,7 +13,8 @@ import { PkiSync, TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
import {
|
||||
AwsCertificateManagerPkiSyncDestinationSection,
|
||||
AzureKeyVaultPkiSyncDestinationSection
|
||||
AzureKeyVaultPkiSyncDestinationSection,
|
||||
ChefPkiSyncDestinationSection
|
||||
} from "./PkiSyncDestinationSection/index";
|
||||
|
||||
const GenericFieldLabel = ({ label, children }: { label: string; children: React.ReactNode }) => (
|
||||
@@ -41,6 +42,9 @@ export const PkiSyncDestinationSection = ({ pkiSync, onEditDestination }: Props)
|
||||
case PkiSync.AzureKeyVault:
|
||||
DestinationComponents = <AzureKeyVaultPkiSyncDestinationSection pkiSync={pkiSync} />;
|
||||
break;
|
||||
case PkiSync.Chef:
|
||||
DestinationComponents = <ChefPkiSyncDestinationSection pkiSync={pkiSync} />;
|
||||
break;
|
||||
default:
|
||||
// For future destinations, return null (no additional fields to show)
|
||||
DestinationComponents = null;
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
const GenericFieldLabel = ({ label, children }: { label: string; children: React.ReactNode }) => (
|
||||
<div className="mb-4">
|
||||
<p className="text-sm font-medium text-mineshaft-300">{label}</p>
|
||||
<div className="text-sm text-mineshaft-300">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
type Props = {
|
||||
pkiSync: TPkiSync;
|
||||
};
|
||||
|
||||
export const ChefPkiSyncDestinationSection = ({ pkiSync }: Props) => {
|
||||
const dataBagName =
|
||||
pkiSync.destinationConfig && "dataBagName" in pkiSync.destinationConfig
|
||||
? pkiSync.destinationConfig.dataBagName
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<GenericFieldLabel label="Chef Data Bag Name">
|
||||
{dataBagName || "Not specified"}
|
||||
</GenericFieldLabel>
|
||||
);
|
||||
};
|
||||
+1
@@ -1,2 +1,3 @@
|
||||
export { AwsCertificateManagerPkiSyncDestinationSection } from "./AwsCertificateManagerPkiSyncDestinationSection";
|
||||
export { AzureKeyVaultPkiSyncDestinationSection } from "./AzureKeyVaultPkiSyncDestinationSection";
|
||||
export { ChefPkiSyncDestinationSection } from "./ChefPkiSyncDestinationSection";
|
||||
|
||||
Reference in New Issue
Block a user