diff --git a/backend/bdd/features/pki/acme/cert-profile.feature b/backend/bdd/features/pki/acme/cert-profile.feature index 1d6dea0c5..f25d0eaf2 100644 --- a/backend/bdd/features/pki/acme/cert-profile.feature +++ b/backend/bdd/features/pki/acme/cert-profile.feature @@ -20,5 +20,3 @@ Feature: ACME Cert Profile Then the value response with jq .certificateProfile.caId should be equal to "{CERT_CA_ID}" Then the value response with jq .certificateProfile.certificateTemplateId should be equal to "{CERT_TEMPLATE_ID}" Then the value response with jq .certificateProfile.enrollmentType should be equal to "acme" - Then the value response with jq .certificateProfile.eabKid should be present - Then the value response with jq .certificateProfile.eabSecret should be present diff --git a/backend/src/ee/services/permission/default-roles.ts b/backend/src/ee/services/permission/default-roles.ts index 34876f739..5e7025f05 100644 --- a/backend/src/ee/services/permission/default-roles.ts +++ b/backend/src/ee/services/permission/default-roles.ts @@ -106,7 +106,9 @@ const buildAdminPermissionRules = () => { ProjectPermissionCertificateProfileActions.Edit, ProjectPermissionCertificateProfileActions.Create, ProjectPermissionCertificateProfileActions.Delete, - ProjectPermissionCertificateProfileActions.IssueCert + ProjectPermissionCertificateProfileActions.IssueCert, + ProjectPermissionCertificateProfileActions.RevealAcmeEabSecret, + ProjectPermissionCertificateProfileActions.RotateAcmeEabSecret ], ProjectPermissionSub.CertificateProfiles ); diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index bb62440c1..74e4554ed 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -116,7 +116,9 @@ export enum ProjectPermissionCertificateProfileActions { Create = "create", Edit = "edit", Delete = "delete", - IssueCert = "issue-cert" + IssueCert = "issue-cert", + RevealAcmeEabSecret = "reveal-acme-eab-secret", + RotateAcmeEabSecret = "rotate-acme-eab-secret" } export enum ProjectPermissionSecretSyncActions { diff --git a/backend/src/server/routes/v1/certificate-profiles-router.ts b/backend/src/server/routes/v1/certificate-profiles-router.ts index 7ad7aaeb1..1a670c254 100644 --- a/backend/src/server/routes/v1/certificate-profiles-router.ts +++ b/backend/src/server/routes/v1/certificate-profiles-router.ts @@ -7,8 +7,8 @@ import { ApiDocsTags } from "@app/lib/api-docs"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; -import { CertStatus } from "@app/services/certificate/certificate-types"; import { EnrollmentType } from "@app/services/certificate-profile/certificate-profile-types"; +import { CertStatus } from "@app/services/certificate/certificate-types"; export const registerCertificateProfilesRouter = async (server: FastifyZodProvider) => { server.route({ @@ -491,4 +491,35 @@ export const registerCertificateProfilesRouter = async (server: FastifyZodProvid return { certificates }; } }); + + server.route({ + method: "GET", + url: "/:id/acme/eab-secret/reveal", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.PkiCertificateProfiles], + params: z.object({ + id: z.string().uuid() + }), + response: { + 200: z.object({ + eabSecret: z.string() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const eabSecret = await server.services.certificateProfile.revealAcmeEabSecret({ + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + profileId: req.params.id + }); + return { eabSecret }; + } + }); }; diff --git a/backend/src/services/certificate-profile/certificate-profile-service.ts b/backend/src/services/certificate-profile/certificate-profile-service.ts index 527ed3c96..f896bb071 100644 --- a/backend/src/services/certificate-profile/certificate-profile-service.ts +++ b/backend/src/services/certificate-profile/certificate-profile-service.ts @@ -782,6 +782,59 @@ export const certificateProfileServiceFactory = ({ }; }; + const revealAcmeEabSecret = async ({ + actor, + actorId, + actorAuthMethod, + actorOrgId, + profileId + }: { + actor: ActorType; + actorId: string; + actorAuthMethod: ActorAuthMethod; + actorOrgId: string; + profileId: string; + }) => { + const profile = await certificateProfileDAL.findByIdWithConfigs(profileId); + if (!profile) { + throw new NotFoundError({ message: "Certificate profile not found" }); + } + + const { permission } = await permissionService.getProjectPermission({ + actor, + actorId, + projectId: profile.projectId, + actorAuthMethod, + actorOrgId, + actionProjectType: ActionProjectType.CertificateManager + }); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateProfileActions.RevealAcmeEabSecret, + ProjectPermissionSub.CertificateProfiles + ); + + if (profile.enrollmentType !== EnrollmentType.ACME) { + throw new ForbiddenRequestError({ + message: "Profile is not configured for ACME enrollment" + }); + } + if (!profile.acmeConfig) { + throw new NotFoundError({ message: "ACME configuration not found for this profile" }); + } + + const certificateManagerKmsId = await getProjectKmsCertificateKeyId({ + projectId: profile.projectId, + projectDAL, + kmsService + }); + + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: certificateManagerKmsId + }); + const eabSecret = await kmsDecryptor({ cipherTextBlob: profile.acmeConfig.encryptedEabSecret }); + return eabSecret.toString(); + }; + return { createProfile, updateProfile, @@ -791,6 +844,7 @@ export const certificateProfileServiceFactory = ({ listProfiles, deleteProfile, getProfileCertificates, - getEstConfigurationByProfile + getEstConfigurationByProfile, + revealAcmeEabSecret }; }; diff --git a/backend/src/services/certificate-profile/certificate-profile-types.ts b/backend/src/services/certificate-profile/certificate-profile-types.ts index 6e1d64fb5..8a89b4d0d 100644 --- a/backend/src/services/certificate-profile/certificate-profile-types.ts +++ b/backend/src/services/certificate-profile/certificate-profile-types.ts @@ -58,6 +58,7 @@ export type TCertificateProfileWithConfigs = TCertificateProfile & { }; acmeConfig?: { id: string; + encryptedEabSecret: Buffer; }; metrics?: TCertificateProfileMetrics; };