mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 07:30:20 +00:00
feat(app-connections, PKI): Cloudflare as DNS provider
This commit is contained in:
@@ -3,14 +3,16 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { appConnectionKeys } from "../queries";
|
||||
import { TCloudflarePagesProject, TCloudflareWorkersScript } from "./types";
|
||||
import { TCloudflarePagesProject, TCloudflareWorkersScript, TCloudflareZone } from "./types";
|
||||
|
||||
const cloudflareConnectionKeys = {
|
||||
all: [...appConnectionKeys.all, "cloudflare"] as const,
|
||||
listPagesProjects: (connectionId: string) =>
|
||||
[...cloudflareConnectionKeys.all, "pages-projects", connectionId] as const,
|
||||
listWorkersScripts: (connectionId: string) =>
|
||||
[...cloudflareConnectionKeys.all, "workers-scripts", connectionId] as const
|
||||
[...cloudflareConnectionKeys.all, "workers-scripts", connectionId] as const,
|
||||
listWorkersZones: (connectionId: string) =>
|
||||
[...cloudflareConnectionKeys.all, "zones", connectionId] as const
|
||||
};
|
||||
|
||||
export const useCloudflareConnectionListPagesProjects = (
|
||||
@@ -62,3 +64,28 @@ export const useCloudflareConnectionListWorkersScripts = (
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const useCloudflareConnectionListZones = (
|
||||
connectionId: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TCloudflareZone[],
|
||||
unknown,
|
||||
TCloudflareZone[],
|
||||
ReturnType<typeof cloudflareConnectionKeys.listWorkersZones>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: cloudflareConnectionKeys.listWorkersZones(connectionId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TCloudflareZone[]>(
|
||||
`/api/v1/app-connections/cloudflare/${connectionId}/cloudflare-zones`
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
@@ -6,3 +6,8 @@ export type TCloudflarePagesProject = {
|
||||
export type TCloudflareWorkersScript = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type TCloudflareZone = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AppConnection } from "../appConnections/enums";
|
||||
import { SshCaStatus } from "../sshCa";
|
||||
import { SshCertTemplateStatus } from "../sshCertificateTemplates";
|
||||
import { CaStatus, InternalCaType } from "./enums";
|
||||
import { AcmeDnsProvider, CaStatus, InternalCaType } from "./enums";
|
||||
|
||||
export const caTypeToNameMap: { [K in InternalCaType]: string } = {
|
||||
[InternalCaType.ROOT]: "Root",
|
||||
@@ -13,6 +14,16 @@ export const caStatusToNameMap: { [K in CaStatus]: string } = {
|
||||
[CaStatus.PENDING_CERTIFICATE]: "Pending Certificate"
|
||||
};
|
||||
|
||||
export const ACME_DNS_PROVIDER_NAME_MAP: Record<AcmeDnsProvider, string> = {
|
||||
[AcmeDnsProvider.ROUTE53]: "Route53",
|
||||
[AcmeDnsProvider.Cloudflare]: "Cloudflare"
|
||||
};
|
||||
|
||||
export const ACME_DNS_PROVIDER_APP_CONNECTION_MAP: Record<AcmeDnsProvider, AppConnection> = {
|
||||
[AcmeDnsProvider.ROUTE53]: AppConnection.AWS,
|
||||
[AcmeDnsProvider.Cloudflare]: AppConnection.Cloudflare
|
||||
};
|
||||
|
||||
export const getCaStatusBadgeVariant = (status: CaStatus | SshCaStatus | SshCertTemplateStatus) => {
|
||||
switch (status) {
|
||||
case CaStatus.ACTIVE:
|
||||
|
||||
@@ -19,5 +19,6 @@ export enum CaRenewalType {
|
||||
}
|
||||
|
||||
export enum AcmeDnsProvider {
|
||||
ROUTE53 = "route53"
|
||||
ROUTE53 = "route53",
|
||||
Cloudflare = "cloudflare"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export type TAcmeCertificateAuthority = {
|
||||
configuration: {
|
||||
dnsAppConnectionId: string;
|
||||
dnsProviderConfig: {
|
||||
provider: AcmeDnsProvider.ROUTE53;
|
||||
provider: AcmeDnsProvider;
|
||||
hostedZoneId: string;
|
||||
};
|
||||
directoryUrl: string;
|
||||
|
||||
+87
-48
@@ -16,7 +16,10 @@ import {
|
||||
Switch
|
||||
} from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useListAvailableAppConnections } from "@app/hooks/api/appConnections";
|
||||
import {
|
||||
TAvailableAppConnection,
|
||||
useListAvailableAppConnections
|
||||
} from "@app/hooks/api/appConnections";
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import {
|
||||
AcmeDnsProvider,
|
||||
@@ -28,6 +31,16 @@ import {
|
||||
} from "@app/hooks/api/ca";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
import { slugSchema } from "@app/lib/schemas";
|
||||
import {
|
||||
ACME_DNS_PROVIDER_APP_CONNECTION_MAP,
|
||||
ACME_DNS_PROVIDER_NAME_MAP
|
||||
} from "@app/hooks/api/ca/constants";
|
||||
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
||||
import {
|
||||
TCloudflareZone,
|
||||
useCloudflareConnectionListZones
|
||||
} from "@app/hooks/api/appConnections/cloudflare";
|
||||
import { SingleValue } from "react-select";
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
@@ -42,7 +55,7 @@ const schema = z
|
||||
id: z.string(),
|
||||
name: z.string()
|
||||
}),
|
||||
// currently specific to Route53 but can be extended to others by differentiating via the provider property
|
||||
// currently specific to Route53 & Cloudflare but can be extended to others by differentiating via the provider property
|
||||
dnsProviderConfig: z.object({
|
||||
provider: z.nativeEnum(AcmeDnsProvider),
|
||||
hostedZoneId: z.string()
|
||||
@@ -105,12 +118,29 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
const caType = watch("type");
|
||||
const dnsProvider = watch("configuration.dnsProviderConfig.provider");
|
||||
|
||||
const { data: availableConnections, isPending } = useListAvailableAppConnections(
|
||||
AppConnection.AWS,
|
||||
{
|
||||
const { data: availableRoute53Connections, isPending: isRoute53Pending } =
|
||||
useListAvailableAppConnections(AppConnection.AWS, {
|
||||
enabled: dnsProvider === AcmeDnsProvider.ROUTE53
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const { data: availableCloudflareConnections, isPending: isCloudflarePending } =
|
||||
useListAvailableAppConnections(AppConnection.Cloudflare, {
|
||||
enabled: dnsProvider === AcmeDnsProvider.Cloudflare
|
||||
});
|
||||
|
||||
const availableConnections: TAvailableAppConnection[] = [
|
||||
...(availableRoute53Connections || []),
|
||||
...(availableCloudflareConnections || [])
|
||||
];
|
||||
|
||||
const isPending = isRoute53Pending || isCloudflarePending;
|
||||
|
||||
const connection = watch("configuration.dnsAppConnection");
|
||||
|
||||
const { data: cloudflareZones = [], isPending: isZonesPending } =
|
||||
useCloudflareConnectionListZones(connection.id, {
|
||||
enabled: dnsProvider === AcmeDnsProvider.Cloudflare && !!connection.id
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (ca) {
|
||||
@@ -138,25 +168,6 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
reset({
|
||||
type: CaType.ACME,
|
||||
name: "",
|
||||
status: CaStatus.ACTIVE,
|
||||
enableDirectIssuance: true,
|
||||
configuration: {
|
||||
dnsAppConnection: {
|
||||
id: "",
|
||||
name: ""
|
||||
},
|
||||
dnsProviderConfig: {
|
||||
provider: AcmeDnsProvider.ROUTE53,
|
||||
hostedZoneId: ""
|
||||
},
|
||||
directoryUrl: "",
|
||||
accountEmail: ""
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [ca, availableConnections]);
|
||||
|
||||
@@ -284,12 +295,11 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
className="w-full"
|
||||
isDisabled={Boolean(ca)}
|
||||
>
|
||||
<SelectItem
|
||||
value={String(AcmeDnsProvider.ROUTE53)}
|
||||
key={AcmeDnsProvider.ROUTE53}
|
||||
>
|
||||
Route53
|
||||
</SelectItem>
|
||||
{Object.values(AcmeDnsProvider).map((provider) => (
|
||||
<SelectItem value={String(provider)} key={provider}>
|
||||
{ACME_DNS_PROVIDER_NAME_MAP[provider]}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
@@ -297,7 +307,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
<Controller
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipText={`${dnsProvider === AcmeDnsProvider.ROUTE53 ? "Route53" : ""} requires an AWS App Connection. This can be created from the Organization Settings page.`}
|
||||
tooltipText={`${ACME_DNS_PROVIDER_NAME_MAP[dnsProvider]} uses the ${APP_CONNECTION_MAP[ACME_DNS_PROVIDER_APP_CONNECTION_MAP[dnsProvider]].name} App Connection. You can create one in the Organization Settings page.`}
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
label="DNS App Connection"
|
||||
@@ -318,21 +328,50 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
control={control}
|
||||
name="configuration.dnsAppConnection"
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="configuration.dnsProviderConfig.hostedZoneId"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Hosted Zone ID"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
isRequired
|
||||
>
|
||||
<Input {...field} placeholder="Z040441124N1GOOMCQYX1" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{dnsProvider === AcmeDnsProvider.ROUTE53 ? (
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="configuration.dnsProviderConfig.hostedZoneId"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Hosted Zone ID"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
isRequired
|
||||
>
|
||||
<Input {...field} placeholder="Z040441124N1GOOMCQYX1" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
) : dnsProvider === AcmeDnsProvider.Cloudflare ? (
|
||||
<Controller
|
||||
name="configuration.dnsProviderConfig.hostedZoneId"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Zone"
|
||||
>
|
||||
<FilterableSelect
|
||||
isLoading={isZonesPending && !!connection.id}
|
||||
isDisabled={!connection.id}
|
||||
value={cloudflareZones.find((zone) => zone.id === value)}
|
||||
onChange={(option) => {
|
||||
onChange((option as SingleValue<TCloudflareZone>)?.id ?? null);
|
||||
}}
|
||||
options={cloudflareZones}
|
||||
placeholder="Select a zone..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.id}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
|
||||
Reference in New Issue
Block a user