Merge pull request #3583 from Infisical/feat/acme-and-external-ca

feat: acme and external CA for PKI
This commit is contained in:
Sheen
2025-05-27 03:47:36 +08:00
committed by GitHub
128 changed files with 10602 additions and 3021 deletions
+10 -1
View File
@@ -68,6 +68,7 @@ export const eventToNameMap: { [K in EventType]: string } = {
[EventType.IMPORT_CA_CERT]: "Import CA certificate",
[EventType.GET_CA_CRL]: "Get CA CRL",
[EventType.ISSUE_CERT]: "Issue certificate",
[EventType.IMPORT_CERT]: "Import certificate",
[EventType.GET_CERT]: "Get certificate",
[EventType.DELETE_CERT]: "Delete certificate",
[EventType.REVOKE_CERT]: "Revoke certificate",
@@ -190,8 +191,16 @@ export const eventToNameMap: { [K in EventType]: string } = {
[EventType.GET_IDENTITY_LDAP_AUTH]: "Retrieved LDAP Auth for identity",
[EventType.REVOKE_IDENTITY_LDAP_AUTH]: "Revoked LDAP Auth for identity",
[EventType.UPDATE_ORG]: "Update Organization",
[EventType.CREATE_PKI_SUBSCRIBER]: "Create PKI subscriber",
[EventType.UPDATE_PKI_SUBSCRIBER]: "Update PKI subscriber",
[EventType.DELETE_PKI_SUBSCRIBER]: "Delete PKI subscriber",
[EventType.GET_PKI_SUBSCRIBER]: "Get PKI subscriber",
[EventType.ISSUE_PKI_SUBSCRIBER_CERT]: "Issue PKI subscriber certificate",
[EventType.SIGN_PKI_SUBSCRIBER_CERT]: "Sign PKI subscriber certificate",
[EventType.AUTOMATED_RENEW_SUBSCRIBER_CERT]: "Automated renew PKI subscriber certificate",
[EventType.LIST_PKI_SUBSCRIBER_CERTS]: "List PKI subscriber certificates",
[EventType.UPDATE_ORG]: "Update Organization",
[EventType.CREATE_PROJECT]: "Create Project",
[EventType.UPDATE_PROJECT]: "Update Project",
[EventType.DELETE_PROJECT]: "Delete Project"
@@ -81,6 +81,7 @@ export enum EventType {
IMPORT_CA_CERT = "import-certificate-authority-cert",
GET_CA_CRL = "get-certificate-authority-crl",
ISSUE_CERT = "issue-cert",
IMPORT_CERT = "import-cert",
GET_CERT = "get-cert",
DELETE_CERT = "delete-cert",
REVOKE_CERT = "revoke-cert",
@@ -184,6 +185,14 @@ export enum EventType {
MICROSOFT_TEAMS_WORKFLOW_INTEGRATION_GET = "microsoft-teams-workflow-integration-get",
MICROSOFT_TEAMS_WORKFLOW_INTEGRATION_LIST = "microsoft-teams-workflow-integration-list",
CREATE_PKI_SUBSCRIBER = "create-pki-subscriber",
UPDATE_PKI_SUBSCRIBER = "update-pki-subscriber",
DELETE_PKI_SUBSCRIBER = "delete-pki-subscriber",
GET_PKI_SUBSCRIBER = "get-pki-subscriber",
ISSUE_PKI_SUBSCRIBER_CERT = "issue-pki-subscriber-cert",
SIGN_PKI_SUBSCRIBER_CERT = "sign-pki-subscriber-cert",
AUTOMATED_RENEW_SUBSCRIBER_CERT = "automated-renew-subscriber-cert",
LIST_PKI_SUBSCRIBER_CERTS = "list-pki-subscriber-certs",
UPDATE_ORG = "update-org",
CREATE_PROJECT = "create-project",
+14 -1
View File
@@ -505,7 +505,8 @@ interface CreateCa {
type: EventType.CREATE_CA;
metadata: {
caId: string;
dn: string;
name: string;
dn?: string;
};
}
@@ -514,12 +515,14 @@ interface GetCa {
metadata: {
caId: string;
dn: string;
name: string;
};
}
interface UpdateCa {
type: EventType.UPDATE_CA;
metadata: {
name: string;
caId: string;
dn: string;
status: CaStatus;
@@ -530,6 +533,7 @@ interface DeleteCa {
type: EventType.DELETE_CA;
metadata: {
caId: string;
name: string;
dn: string;
};
}
@@ -583,6 +587,14 @@ interface IssueCert {
serialNumber: string;
};
}
interface ImportCert {
type: EventType.IMPORT_CERT;
metadata: {
certId: string;
cn: string;
serialNumber: string;
};
}
interface GetCert {
type: EventType.GET_CERT;
@@ -895,6 +907,7 @@ export type Event =
| ImportCaCert
| GetCaCrl
| IssueCert
| ImportCert
| GetCert
| DeleteCert
| RevokeCert
+4 -4
View File
@@ -1,10 +1,10 @@
import { SshCaStatus } from "../sshCa";
import { SshCertTemplateStatus } from "../sshCertificateTemplates";
import { CaStatus, CaType } from "./enums";
import { CaStatus, InternalCaType } from "./enums";
export const caTypeToNameMap: { [K in CaType]: string } = {
[CaType.ROOT]: "Root",
[CaType.INTERMEDIATE]: "Intermediate"
export const caTypeToNameMap: { [K in InternalCaType]: string } = {
[InternalCaType.ROOT]: "Root",
[InternalCaType.INTERMEDIATE]: "Intermediate"
};
export const caStatusToNameMap: { [K in CaStatus]: string } = {
+9
View File
@@ -1,4 +1,9 @@
export enum CaType {
INTERNAL = "internal",
ACME = "acme"
}
export enum InternalCaType {
ROOT = "root",
INTERMEDIATE = "intermediate"
}
@@ -12,3 +17,7 @@ export enum CaStatus {
export enum CaRenewalType {
EXISTING = "existing"
}
export enum AcmeDnsProvider {
ROUTE53 = "route53"
}
+5 -2
View File
@@ -1,4 +1,4 @@
export { CaRenewalType, CaStatus, CaType } from "./enums";
export { AcmeDnsProvider, CaRenewalType, CaStatus, CaType, InternalCaType } from "./enums";
export {
useCreateCa,
useCreateCertificate,
@@ -9,10 +9,13 @@ export {
useUpdateCa
} from "./mutations";
export {
useGetCa,
useGetCaById,
useGetCaCert,
useGetCaCerts,
useGetCaCertTemplates,
useGetCaCrls,
useGetCaCsr
useGetCaCsr,
useListCasByProjectId,
useListCasByTypeAndProjectId
} from "./queries";
+49 -32
View File
@@ -3,64 +3,78 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { workspaceKeys } from "../workspace";
import { CaType } from "./enums";
import { caKeys } from "./queries";
import {
TCertificateAuthority,
TCreateCaDTO,
TCreateCertificateAuthorityDTO,
TCreateCertificateDTO,
TCreateCertificateResponse,
TDeleteCaDTO,
TDeleteCertificateAuthorityDTO,
TImportCaCertificateDTO,
TImportCaCertificateResponse,
TRenewCaDTO,
TRenewCaResponse,
TSignIntermediateDTO,
TSignIntermediateResponse,
TUpdateCaDTO
TUnifiedCertificateAuthority,
TUpdateCertificateAuthorityDTO
} from "./types";
export const useCreateCa = () => {
export const useUpdateCa = () => {
const queryClient = useQueryClient();
return useMutation<TCertificateAuthority, object, TCreateCaDTO>({
mutationFn: async (body) => {
const {
data: { ca }
} = await apiRequest.post<{ ca: TCertificateAuthority }>("/api/v1/pki/ca/", body);
return ca;
return useMutation<TUnifiedCertificateAuthority, object, TUpdateCertificateAuthorityDTO>({
mutationFn: async ({ caName, ...body }) => {
const { data } = await apiRequest.patch<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${body.type}/${caName}`,
body
);
return data;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceCas({ projectSlug }) });
onSuccess: ({ projectId, type }) => {
queryClient.invalidateQueries({
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId)
});
}
});
};
export const useUpdateCa = () => {
export const useCreateCa = () => {
const queryClient = useQueryClient();
return useMutation<TCertificateAuthority, object, TUpdateCaDTO>({
mutationFn: async ({ caId, projectSlug, ...body }) => {
const {
data: { ca }
} = await apiRequest.patch<{ ca: TCertificateAuthority }>(`/api/v1/pki/ca/${caId}`, body);
return ca;
return useMutation<TUnifiedCertificateAuthority, object, TCreateCertificateAuthorityDTO>({
mutationFn: async (body) => {
const { data } = await apiRequest.post<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${body.type}`,
body
);
return data;
},
onSuccess: ({ id }, { projectSlug }) => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceCas({ projectSlug }) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaById(id) });
onSuccess: (_, { type, projectId }) => {
queryClient.invalidateQueries({
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId)
});
}
});
};
export const useDeleteCa = () => {
const queryClient = useQueryClient();
return useMutation<TCertificateAuthority, object, TDeleteCaDTO>({
mutationFn: async ({ caId }) => {
const {
data: { ca }
} = await apiRequest.delete<{ ca: TCertificateAuthority }>(`/api/v1/pki/ca/${caId}`);
return ca;
return useMutation<TUnifiedCertificateAuthority, object, TDeleteCertificateAuthorityDTO>({
mutationFn: async ({ caName, type, projectId }) => {
const { data } = await apiRequest.delete<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${type}/${caName}`,
{
data: {
projectId
}
}
);
return data;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceCas({ projectSlug }) });
onSuccess: (_, { type, projectId }) => {
queryClient.invalidateQueries({
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId)
});
}
});
};
@@ -78,7 +92,7 @@ export const useSignIntermediate = () => {
});
};
export const useImportCaCertificate = () => {
export const useImportCaCertificate = (projectId: string) => {
const queryClient = useQueryClient();
return useMutation<TImportCaCertificateResponse, object, TImportCaCertificateDTO>({
mutationFn: async ({ caId, ...body }) => {
@@ -92,6 +106,9 @@ export const useImportCaCertificate = () => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceCas({ projectSlug }) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaCerts(caId) });
queryClient.invalidateQueries({ queryKey: caKeys.getCaCert(caId) });
queryClient.invalidateQueries({
queryKey: caKeys.listCasByTypeAndProjectId(CaType.INTERNAL, projectId)
});
}
});
};
+52 -1
View File
@@ -3,10 +3,14 @@ import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { TCertificateTemplate } from "../certificateTemplates/types";
import { TCertificateAuthority } from "./types";
import { CaType } from "./enums";
import { TCertificateAuthority, TUnifiedCertificateAuthority } from "./types";
export const caKeys = {
getCaById: (caId: string) => [{ caId }, "ca"],
getCaByNameAndProjectId: (caName: string, projectId: string) => [{ caName, projectId }, "ca"],
listCasByTypeAndProjectId: (type: CaType, projectId: string) => [{ type, projectId }, "cas"],
listCasByProjectId: (projectId: string) => [{ projectId }, "cas"],
getCaCerts: (caId: string) => [{ caId }, "ca-cert"],
getCaCrls: (caId: string) => [{ caId }, "ca-crls"],
getCaCert: (caId: string) => [{ caId }, "ca-cert"],
@@ -16,6 +20,53 @@ export const caKeys = {
getCaEstConfig: (caId: string) => [{ caId }, "ca-est-config"]
};
export const useGetCa = ({
caName,
projectId,
type
}: {
caName: string;
projectId: string;
type: CaType;
}) => {
return useQuery({
queryKey: caKeys.getCaByNameAndProjectId(caName, projectId),
queryFn: async () => {
const { data } = await apiRequest.get<TUnifiedCertificateAuthority>(
`/api/v1/pki/ca/${type}/${caName}?projectId=${projectId}`
);
return data;
},
enabled: Boolean(caName && projectId && type)
});
};
export const useListCasByTypeAndProjectId = (type: CaType, projectId: string) => {
return useQuery({
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId),
queryFn: async () => {
const { data } = await apiRequest.get<TUnifiedCertificateAuthority[]>(
`/api/v1/pki/ca/${type}?projectId=${projectId}`
);
return data;
}
});
};
export const useListCasByProjectId = (projectId: string) => {
return useQuery({
queryKey: caKeys.listCasByProjectId(projectId),
queryFn: async () => {
const { data } = await apiRequest.get<{
certificateAuthorities: TUnifiedCertificateAuthority[];
}>(`/api/v2/pki/ca?projectId=${projectId}`);
return data.certificateAuthorities;
}
});
};
export const useGetCaById = (caId: string) => {
return useQuery({
queryKey: caKeys.getCaById(caId),
+64 -18
View File
@@ -1,11 +1,73 @@
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "../certificates/enums";
import { CaRenewalType, CaStatus, CaType } from "./enums";
import { AcmeDnsProvider, CaRenewalType, CaStatus, CaType, InternalCaType } from "./enums";
export type TAcmeCertificateAuthority = {
id: string;
projectId: string;
type: CaType.ACME;
status: CaStatus;
name: string;
enableDirectIssuance: boolean;
configuration: {
dnsAppConnectionId: string;
dnsProviderConfig: {
provider: AcmeDnsProvider.ROUTE53;
hostedZoneId: string;
};
directoryUrl: string;
accountEmail: string;
};
};
export type TInternalCertificateAuthority = {
id: string;
projectId: string;
type: CaType.INTERNAL;
status: CaStatus;
name: string;
enableDirectIssuance: boolean;
configuration: {
type: InternalCaType;
friendlyName?: string;
commonName: string;
organization: string;
ou: string;
country: string;
province: string;
locality: string;
maxPathLength: number;
keyAlgorithm: CertKeyAlgorithm;
notAfter?: string;
notBefore?: string;
dn?: string;
parentCaId?: string;
serialNumber?: string;
activeCaCertId?: string;
};
};
export type TUnifiedCertificateAuthority =
| TAcmeCertificateAuthority
| TInternalCertificateAuthority;
export type TCreateCertificateAuthorityDTO = Omit<TUnifiedCertificateAuthority, "id">;
export type TUpdateCertificateAuthorityDTO = Partial<TUnifiedCertificateAuthority> & {
caName: string;
projectId: string;
type: CaType;
};
export type TDeleteCertificateAuthorityDTO = {
caName: string;
type: CaType;
projectId: string;
};
export type TCertificateAuthority = {
id: string;
parentCaId?: string;
projectId: string;
type: CaType;
type: InternalCaType;
status: CaStatus;
friendlyName: string;
organization: string;
@@ -25,22 +87,6 @@ export type TCertificateAuthority = {
updatedAt: string;
};
export type TCreateCaDTO = {
projectSlug: string;
type: string;
friendlyName?: string;
organization: string;
ou: string;
country: string;
province: string;
locality: string;
commonName: string;
notAfter?: string;
maxPathLength: number;
keyAlgorithm: CertKeyAlgorithm;
requireTemplateForIssuance: boolean;
};
export type TUpdateCaDTO = {
projectSlug: string;
caId: string;
@@ -1,2 +1,2 @@
export { useDeleteCert, useRevokeCert } from "./mutations";
export { useDeleteCert, useImportCertificate, useRevokeCert } from "./mutations";
export { useGetCert, useGetCertBody } from "./queries";
@@ -4,7 +4,13 @@ import { apiRequest } from "@app/config/request";
import { pkiSubscriberKeys } from "../pkiSubscriber/queries";
import { workspaceKeys } from "../workspace";
import { TCertificate, TDeleteCertDTO, TRevokeCertDTO } from "./types";
import {
TCertificate,
TDeleteCertDTO,
TImportCertificateDTO,
TImportCertificateResponse,
TRevokeCertDTO
} from "./types";
export const useDeleteCert = () => {
const queryClient = useQueryClient();
@@ -49,3 +55,21 @@ export const useRevokeCert = () => {
}
});
};
export const useImportCertificate = () => {
const queryClient = useQueryClient();
return useMutation<TImportCertificateResponse, object, TImportCertificateDTO>({
mutationFn: async (body) => {
const { data } = await apiRequest.post<TImportCertificateResponse>(
"/api/v1/pki/certificates/import-certificate",
body
);
return data;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries({
queryKey: workspaceKeys.forWorkspaceCertificates(projectSlug)
});
}
});
};
@@ -25,3 +25,21 @@ export type TRevokeCertDTO = {
serialNumber: string;
revocationReason: string;
};
export type TImportCertificateDTO = {
projectSlug: string;
certificatePem: string;
privateKeyPem: string;
chainPem: string;
pkiCollectionId?: string;
friendlyName?: string;
};
export type TImportCertificateResponse = {
certificate: string;
certificateChain: string;
privateKey: string;
serialNumber: string;
};
@@ -2,6 +2,7 @@ export {
useCreatePkiSubscriber,
useDeletePkiSubscriber,
useIssuePkiSubscriberCert,
useOrderPkiSubscriberCert,
useUpdatePkiSubscriber
} from "./mutations";
export { useGetPkiSubscriber, useGetPkiSubscriberCertificates } from "./queries";
@@ -108,3 +108,17 @@ export const useIssuePkiSubscriberCert = () => {
}
});
};
export const useOrderPkiSubscriberCert = () => {
return useMutation<{ message: string }, object, TIssuePkiSubscriberCertDTO>({
mutationFn: async ({ subscriberName, projectId }) => {
const { data } = await apiRequest.post(
`/api/v1/pki/subscribers/${subscriberName}/order-certificate`,
{
projectId
}
);
return data;
}
});
};
@@ -1,6 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { TReactQueryOptions } from "@app/types/reactQuery";
import { TCertificate } from "../certificates/types";
import { TPkiSubscriber } from "./types";
@@ -38,13 +39,16 @@ export const pkiSubscriberKeys = {
] as const
};
export const useGetPkiSubscriber = ({
subscriberName,
projectId
}: {
subscriberName: string;
projectId: string;
}) => {
export const useGetPkiSubscriber = (
{
subscriberName,
projectId
}: {
subscriberName: string;
projectId: string;
},
options?: TReactQueryOptions["options"]
) => {
return useQuery({
queryKey: pkiSubscriberKeys.getPkiSubscriber({ subscriberName, projectId }),
queryFn: async () => {
@@ -58,21 +62,25 @@ export const useGetPkiSubscriber = ({
);
return pkiSubscriber;
},
enabled: Boolean(subscriberName) && Boolean(projectId)
enabled: Boolean(subscriberName) && Boolean(projectId),
...options
});
};
export const useGetPkiSubscriberCertificates = ({
subscriberName,
projectId,
offset,
limit
}: {
subscriberName: string;
projectId: string;
offset: number;
limit: number;
}) => {
export const useGetPkiSubscriberCertificates = (
{
subscriberName,
projectId,
offset,
limit
}: {
subscriberName: string;
projectId: string;
offset: number;
limit: number;
},
options?: TReactQueryOptions["options"]
) => {
return useQuery({
queryKey: pkiSubscriberKeys.specificPkiSubscriberCertificates({
subscriberName,
@@ -97,6 +105,7 @@ export const useGetPkiSubscriberCertificates = ({
);
return { certificates, totalCount };
},
enabled: Boolean(subscriberName) && Boolean(projectId)
enabled: Boolean(subscriberName) && Boolean(projectId),
...options
});
};
+22 -2
View File
@@ -5,6 +5,11 @@ export enum PkiSubscriberStatus {
DISABLED = "disabled"
}
export enum SubscriberOperationStatus {
SUCCESS = "success",
FAILED = "failed"
}
export type TPkiSubscriber = {
id: string;
projectId: string;
@@ -12,10 +17,16 @@ export type TPkiSubscriber = {
name: string;
commonName: string;
status: PkiSubscriberStatus;
ttl: string;
ttl?: string;
subjectAlternativeNames: string[];
keyUsages: CertKeyUsage[];
extendedKeyUsages: CertExtendedKeyUsage[];
supportsImmediateCertIssuance?: boolean;
enableAutoRenewal?: boolean;
autoRenewalPeriodInDays?: number;
lastOperationStatus?: SubscriberOperationStatus;
lastOperationMessage?: string;
lastOperationAt?: string;
};
export type TCreatePkiSubscriberDTO = {
@@ -23,10 +34,12 @@ export type TCreatePkiSubscriberDTO = {
caId: string;
name: string;
commonName: string;
ttl: string;
ttl?: string;
subjectAlternativeNames: string[];
keyUsages: CertKeyUsage[];
extendedKeyUsages: CertExtendedKeyUsage[];
enableAutoRenewal?: boolean;
autoRenewalPeriodInDays?: number;
};
export type TUpdatePkiSubscriberDTO = {
@@ -40,6 +53,8 @@ export type TUpdatePkiSubscriberDTO = {
subjectAlternativeNames?: string[];
keyUsages?: CertKeyUsage[];
extendedKeyUsages?: CertExtendedKeyUsage[];
enableAutoRenewal?: boolean;
autoRenewalPeriodInDays?: number;
};
export type TDeletePkiSubscriberDTO = {
@@ -51,3 +66,8 @@ export type TIssuePkiSubscriberCertDTO = {
subscriberName: string;
projectId: string;
};
export type TOrderPkiSubscriberCertDTO = {
subscriberName: string;
projectId: string;
};