mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 14:26:25 +00:00
Add challenge
This commit is contained in:
@@ -18,6 +18,7 @@ Feature: Order
|
|||||||
Then I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order
|
Then I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order
|
||||||
Then the value order.authorizations[0].uri with jq . should match pattern {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/authorizations/(.+)
|
Then the value order.authorizations[0].uri with jq . should match pattern {BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/authorizations/(.+)
|
||||||
Then the value order.authorizations[0].body with jq .status should be equal to "pending"
|
Then the value order.authorizations[0].body with jq .status should be equal to "pending"
|
||||||
|
Then the value order.authorizations[0].body with jq .challenge should be equal to "pending"
|
||||||
Then the value order.authorizations[0].body with jq .identifier should be equal to json
|
Then the value order.authorizations[0].body with jq .identifier should be equal to json
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -186,8 +186,6 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: DeactivateAcmeAccountResponseSchema
|
200: DeactivateAcmeAccountResponseSchema
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: replace with verify ACME signature here instead
|
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const { payload, profileId, accountId } = await validateExistingAccount({
|
const { payload, profileId, accountId } = await validateExistingAccount({
|
||||||
req,
|
req,
|
||||||
@@ -223,8 +221,6 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
201: AcmeOrderResourceSchema
|
201: AcmeOrderResourceSchema
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: replace with verify ACME signature here instead
|
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const { profileId, accountId, payload } = await validateExistingAccount({
|
const { profileId, accountId, payload } = await validateExistingAccount({
|
||||||
req,
|
req,
|
||||||
@@ -262,8 +258,6 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: AcmeOrderResourceSchema
|
200: AcmeOrderResourceSchema
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: replace with verify ACME signature here instead
|
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const { profileId, accountId } = await validateExistingAccount({
|
const { profileId, accountId } = await validateExistingAccount({
|
||||||
req,
|
req,
|
||||||
@@ -301,8 +295,6 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: AcmeOrderResourceSchema
|
200: AcmeOrderResourceSchema
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: replace with verify ACME signature here instead
|
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const { profileId, accountId, payload } = await validateExistingAccount({
|
const { profileId, accountId, payload } = await validateExistingAccount({
|
||||||
req,
|
req,
|
||||||
@@ -378,8 +370,6 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: z.string()
|
200: z.string()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: replace with verify ACME signature here instead
|
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const { profileId, accountId } = await validateExistingAccount({
|
const { profileId, accountId } = await validateExistingAccount({
|
||||||
req,
|
req,
|
||||||
@@ -413,8 +403,6 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: GetAcmeAuthorizationResponseSchema
|
200: GetAcmeAuthorizationResponseSchema
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: replace with verify ACME signature here instead
|
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const { profileId, accountId, payload } = await validateExistingAccount({ req });
|
const { profileId, accountId, payload } = await validateExistingAccount({ req });
|
||||||
if (payload !== "") {
|
if (payload !== "") {
|
||||||
@@ -451,10 +439,16 @@ export const registerPkiAcmeRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: RespondToAcmeChallengeResponseSchema
|
200: RespondToAcmeChallengeResponseSchema
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: replace with verify ACME signature here instead
|
handler: async (req, res) => {
|
||||||
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
const { profileId, accountId, payload } = await validateExistingAccount({ req });
|
||||||
handler: async (req) => {
|
if (payload !== "") {
|
||||||
const challenge = await server.services.pkiAcme.respondToAcmeChallenge(req.params.profileId, req.params.authzId);
|
throw new AcmeMalformedError({ detail: "Payload should be empty" });
|
||||||
|
}
|
||||||
|
return sendAcmeResponse(
|
||||||
|
res,
|
||||||
|
profileId,
|
||||||
|
await server.services.pkiAcme.respondToAcmeChallenge({ profileId, authzId: req.params.authzId })
|
||||||
|
);
|
||||||
return challenge;
|
return challenge;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TPkiAcmeChallengeDALFactory = ReturnType<typeof pkiAcmeChallengeDALFactory>;
|
||||||
|
|
||||||
|
export const pkiAcmeChallengeDALFactory = (db: TDbClient) => {
|
||||||
|
const pkiAcmeChallengeOrm = ormify(db, TableName.PkiAcmeChallenge);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...pkiAcmeChallengeOrm
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -21,6 +21,12 @@ export enum AcmeAuthStatus {
|
|||||||
Revoked = "revoked"
|
Revoked = "revoked"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AcmeChallengeType {
|
||||||
|
HTTP_01 = "http-01",
|
||||||
|
DNS_01 = "dns-01",
|
||||||
|
TLS_ALPN_01 = "tls-alpn-01"
|
||||||
|
}
|
||||||
|
|
||||||
export const ProtectedHeaderSchema = z
|
export const ProtectedHeaderSchema = z
|
||||||
.object({
|
.object({
|
||||||
alg: z.string(),
|
alg: z.string(),
|
||||||
@@ -136,7 +142,7 @@ export const GetAcmeAuthorizationResponseSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
challenges: z.array(
|
challenges: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
type: z.string(),
|
type: z.enum(Object.values(AcmeChallengeType) as [string, ...string[]]),
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
status: z.string(),
|
status: z.string(),
|
||||||
token: z.string(),
|
token: z.string(),
|
||||||
@@ -146,7 +152,7 @@ export const GetAcmeAuthorizationResponseSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const RespondToAcmeChallengeResponseSchema = z.object({
|
export const RespondToAcmeChallengeResponseSchema = z.object({
|
||||||
type: z.string(),
|
type: z.enum(Object.values(AcmeChallengeType) as [string, ...string[]]),
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
status: z.string(),
|
status: z.string(),
|
||||||
token: z.string(),
|
token: z.string(),
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { errors, flattenedVerify, FlattenedVerifyResult, importJWK, JWSHeaderPar
|
|||||||
import { z, ZodError } from "zod";
|
import { z, ZodError } from "zod";
|
||||||
import { TPkiAcmeAccountDALFactory } from "./pki-acme-account-dal";
|
import { TPkiAcmeAccountDALFactory } from "./pki-acme-account-dal";
|
||||||
import { TPkiAcmeAuthDALFactory } from "./pki-acme-auth-dal";
|
import { TPkiAcmeAuthDALFactory } from "./pki-acme-auth-dal";
|
||||||
|
import { TPkiAcmeChallengeDALFactory } from "./pki-acme-challenge-dal";
|
||||||
import {
|
import {
|
||||||
AcmeAccountDoesNotExistError,
|
AcmeAccountDoesNotExistError,
|
||||||
AcmeBadPublicKeyError,
|
AcmeBadPublicKeyError,
|
||||||
@@ -28,6 +29,7 @@ import { TPkiAcmeOrderAuthDALFactory } from "./pki-acme-order-auth-dal";
|
|||||||
import { TPkiAcmeOrderDALFactory } from "./pki-acme-order-dal";
|
import { TPkiAcmeOrderDALFactory } from "./pki-acme-order-dal";
|
||||||
import {
|
import {
|
||||||
AcmeAuthStatus,
|
AcmeAuthStatus,
|
||||||
|
AcmeChallengeType,
|
||||||
AcmeIdentifierType,
|
AcmeIdentifierType,
|
||||||
AcmeOrderStatus,
|
AcmeOrderStatus,
|
||||||
CreateAcmeAccountBodySchema,
|
CreateAcmeAccountBodySchema,
|
||||||
@@ -58,6 +60,7 @@ type TPkiAcmeServiceFactoryDep = {
|
|||||||
acmeOrderDAL: Pick<TPkiAcmeOrderDALFactory, "create" | "transaction" | "findByAccountAndOrderIdWithAuthorizations">;
|
acmeOrderDAL: Pick<TPkiAcmeOrderDALFactory, "create" | "transaction" | "findByAccountAndOrderIdWithAuthorizations">;
|
||||||
acmeAuthDAL: Pick<TPkiAcmeAuthDALFactory, "create" | "findByAccountIdAndAuthIdWithChallenges">;
|
acmeAuthDAL: Pick<TPkiAcmeAuthDALFactory, "create" | "findByAccountIdAndAuthIdWithChallenges">;
|
||||||
acmeOrderAuthDAL: Pick<TPkiAcmeOrderAuthDALFactory, "insertMany">;
|
acmeOrderAuthDAL: Pick<TPkiAcmeOrderAuthDALFactory, "insertMany">;
|
||||||
|
acmeChallengeDAL: Pick<TPkiAcmeChallengeDALFactory, "create">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pkiAcmeServiceFactory = ({
|
export const pkiAcmeServiceFactory = ({
|
||||||
@@ -65,7 +68,8 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
acmeAccountDAL,
|
acmeAccountDAL,
|
||||||
acmeOrderDAL,
|
acmeOrderDAL,
|
||||||
acmeAuthDAL,
|
acmeAuthDAL,
|
||||||
acmeOrderAuthDAL
|
acmeOrderAuthDAL,
|
||||||
|
acmeChallengeDAL
|
||||||
}: TPkiAcmeServiceFactoryDep): TPkiAcmeServiceFactory => {
|
}: TPkiAcmeServiceFactoryDep): TPkiAcmeServiceFactory => {
|
||||||
const validateAcmeProfile = async (profileId: string): Promise<TCertificateProfileWithConfigs> => {
|
const validateAcmeProfile = async (profileId: string): Promise<TCertificateProfileWithConfigs> => {
|
||||||
const profile = await certificateProfileDAL.findById(profileId);
|
const profile = await certificateProfileDAL.findById(profileId);
|
||||||
@@ -376,7 +380,7 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
payload.identifiers.map(async (identifier) => {
|
payload.identifiers.map(async (identifier) => {
|
||||||
if (identifier.type === AcmeIdentifierType.DNS) {
|
if (identifier.type === AcmeIdentifierType.DNS) {
|
||||||
// TODO: reuse existing authorizations for this identifier if they exist
|
// TODO: reuse existing authorizations for this identifier if they exist
|
||||||
return await acmeAuthDAL.create(
|
const auth = await acmeAuthDAL.create(
|
||||||
{
|
{
|
||||||
accountId: account.id,
|
accountId: account.id,
|
||||||
status: AcmeAuthStatus.Pending,
|
status: AcmeAuthStatus.Pending,
|
||||||
@@ -391,6 +395,15 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
// TODO: support other challenge types here. Currently only HTTP-01 is supported.
|
||||||
|
await acmeChallengeDAL.create(
|
||||||
|
{
|
||||||
|
authId: auth.id,
|
||||||
|
type: AcmeChallengeType.HTTP_01
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
return auth;
|
||||||
} else {
|
} else {
|
||||||
throw new AcmeUnsupportedIdentifierError({ detail: "Only DNS identifiers are supported" });
|
throw new AcmeUnsupportedIdentifierError({ detail: "Only DNS identifiers are supported" });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user