mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 05:26:43 +00:00
Audit logs for challenges
This commit is contained in:
@@ -122,6 +122,11 @@ export const pkiAcmeChallengeDALFactory = (db: TDbClient) => {
|
|||||||
const result = await (tx || db)(TableName.PkiAcmeChallenge)
|
const result = await (tx || db)(TableName.PkiAcmeChallenge)
|
||||||
.join(TableName.PkiAcmeAuth, `${TableName.PkiAcmeChallenge}.authId`, `${TableName.PkiAcmeAuth}.id`)
|
.join(TableName.PkiAcmeAuth, `${TableName.PkiAcmeChallenge}.authId`, `${TableName.PkiAcmeAuth}.id`)
|
||||||
.join(TableName.PkiAcmeAccount, `${TableName.PkiAcmeAuth}.accountId`, `${TableName.PkiAcmeAccount}.id`)
|
.join(TableName.PkiAcmeAccount, `${TableName.PkiAcmeAuth}.accountId`, `${TableName.PkiAcmeAccount}.id`)
|
||||||
|
.join(
|
||||||
|
TableName.PkiCertificateProfile,
|
||||||
|
`${TableName.PkiAcmeAccount}.profileId`,
|
||||||
|
`${TableName.PkiCertificateProfile}.id`
|
||||||
|
)
|
||||||
.select(
|
.select(
|
||||||
selectAllTableCols(TableName.PkiAcmeChallenge),
|
selectAllTableCols(TableName.PkiAcmeChallenge),
|
||||||
db.ref("id").withSchema(TableName.PkiAcmeAuth).as("authId"),
|
db.ref("id").withSchema(TableName.PkiAcmeAuth).as("authId"),
|
||||||
@@ -131,7 +136,9 @@ export const pkiAcmeChallengeDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("identifierValue").withSchema(TableName.PkiAcmeAuth).as("authIdentifierValue"),
|
db.ref("identifierValue").withSchema(TableName.PkiAcmeAuth).as("authIdentifierValue"),
|
||||||
db.ref("expiresAt").withSchema(TableName.PkiAcmeAuth).as("authExpiresAt"),
|
db.ref("expiresAt").withSchema(TableName.PkiAcmeAuth).as("authExpiresAt"),
|
||||||
db.ref("id").withSchema(TableName.PkiAcmeAccount).as("accountId"),
|
db.ref("id").withSchema(TableName.PkiAcmeAccount).as("accountId"),
|
||||||
db.ref("publicKeyThumbprint").withSchema(TableName.PkiAcmeAccount).as("accountPublicKeyThumbprint")
|
db.ref("publicKeyThumbprint").withSchema(TableName.PkiAcmeAccount).as("accountPublicKeyThumbprint"),
|
||||||
|
db.ref("profileId").withSchema(TableName.PkiAcmeAccount).as("profileId"),
|
||||||
|
db.ref("projectId").withSchema(TableName.PkiCertificateProfile).as("projectId")
|
||||||
)
|
)
|
||||||
// For all challenges, acquire update lock on the auth to avoid race conditions
|
// For all challenges, acquire update lock on the auth to avoid race conditions
|
||||||
.forUpdate(TableName.PkiAcmeAuth)
|
.forUpdate(TableName.PkiAcmeAuth)
|
||||||
@@ -149,6 +156,8 @@ export const pkiAcmeChallengeDALFactory = (db: TDbClient) => {
|
|||||||
authExpiresAt,
|
authExpiresAt,
|
||||||
accountId,
|
accountId,
|
||||||
accountPublicKeyThumbprint,
|
accountPublicKeyThumbprint,
|
||||||
|
profileId,
|
||||||
|
projectId,
|
||||||
...challenge
|
...challenge
|
||||||
} = result;
|
} = result;
|
||||||
return {
|
return {
|
||||||
@@ -161,7 +170,11 @@ export const pkiAcmeChallengeDALFactory = (db: TDbClient) => {
|
|||||||
expiresAt: authExpiresAt,
|
expiresAt: authExpiresAt,
|
||||||
account: {
|
account: {
|
||||||
id: accountId,
|
id: accountId,
|
||||||
publicKeyThumbprint: accountPublicKeyThumbprint
|
publicKeyThumbprint: accountPublicKeyThumbprint,
|
||||||
|
project: {
|
||||||
|
id: projectId
|
||||||
|
},
|
||||||
|
profileId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import { getConfig } from "@app/lib/config/env";
|
|||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { isPrivateIp } from "@app/lib/ip/ipRange";
|
import { isPrivateIp } from "@app/lib/ip/ipRange";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { EventType, TAuditLogServiceFactory } from "../audit-log/audit-log-types";
|
||||||
import { TPkiAcmeChallengeDALFactory } from "./pki-acme-challenge-dal";
|
import { TPkiAcmeChallengeDALFactory } from "./pki-acme-challenge-dal";
|
||||||
import {
|
import {
|
||||||
AcmeConnectionError,
|
AcmeConnectionError,
|
||||||
@@ -25,10 +27,12 @@ type TPkiAcmeChallengeServiceFactoryDep = {
|
|||||||
| "markAsInvalidCascadeById"
|
| "markAsInvalidCascadeById"
|
||||||
| "updateById"
|
| "updateById"
|
||||||
>;
|
>;
|
||||||
|
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pkiAcmeChallengeServiceFactory = ({
|
export const pkiAcmeChallengeServiceFactory = ({
|
||||||
acmeChallengeDAL
|
acmeChallengeDAL,
|
||||||
|
auditLogService
|
||||||
}: TPkiAcmeChallengeServiceFactoryDep): TPkiAcmeChallengeServiceFactory => {
|
}: TPkiAcmeChallengeServiceFactoryDep): TPkiAcmeChallengeServiceFactory => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const markChallengeAsReady = async (challengeId: string): Promise<TPkiAcmeChallenges> => {
|
const markChallengeAsReady = async (challengeId: string): Promise<TPkiAcmeChallenges> => {
|
||||||
@@ -113,7 +117,25 @@ export const pkiAcmeChallengeServiceFactory = ({
|
|||||||
}
|
}
|
||||||
logger.info({ challengeId }, "ACME challenge response is correct, marking challenge as valid");
|
logger.info({ challengeId }, "ACME challenge response is correct, marking challenge as valid");
|
||||||
await acmeChallengeDAL.markAsValidCascadeById(challengeId);
|
await acmeChallengeDAL.markAsValidCascadeById(challengeId);
|
||||||
|
await auditLogService.createAuditLog({
|
||||||
|
projectId: challenge.auth.account.project.id,
|
||||||
|
actor: {
|
||||||
|
type: ActorType.ACME_ACCOUNT,
|
||||||
|
metadata: {
|
||||||
|
profileId: challenge.auth.account.profileId,
|
||||||
|
accountId: challenge.auth.account.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
type: EventType.PASS_ACME_CHALLENGE,
|
||||||
|
metadata: {
|
||||||
|
challengeId,
|
||||||
|
type: challenge.type as AcmeChallengeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (exp) {
|
} catch (exp) {
|
||||||
|
let finalAttempt = false;
|
||||||
if (retryCount >= 2) {
|
if (retryCount >= 2) {
|
||||||
logger.error(
|
logger.error(
|
||||||
exp,
|
exp,
|
||||||
@@ -121,35 +143,59 @@ export const pkiAcmeChallengeServiceFactory = ({
|
|||||||
);
|
);
|
||||||
// This is the last attempt to validate the challenge response, if it fails, we mark the challenge as invalid
|
// This is the last attempt to validate the challenge response, if it fails, we mark the challenge as invalid
|
||||||
await acmeChallengeDAL.markAsInvalidCascadeById(challengeId);
|
await acmeChallengeDAL.markAsInvalidCascadeById(challengeId);
|
||||||
|
finalAttempt = true;
|
||||||
}
|
}
|
||||||
// Properly type and inspect the error
|
try {
|
||||||
if (axios.isAxiosError(exp)) {
|
// Properly type and inspect the error
|
||||||
const axiosError = exp as AxiosError;
|
if (axios.isAxiosError(exp)) {
|
||||||
const errorCode = axiosError.code;
|
const axiosError = exp as AxiosError;
|
||||||
const errorMessage = axiosError.message;
|
const errorCode = axiosError.code;
|
||||||
|
const errorMessage = axiosError.message;
|
||||||
|
|
||||||
if (errorCode === "ECONNREFUSED" || errorMessage.includes("ECONNREFUSED")) {
|
if (errorCode === "ECONNREFUSED" || errorMessage.includes("ECONNREFUSED")) {
|
||||||
throw new AcmeConnectionError({ message: "Connection refused" });
|
throw new AcmeConnectionError({ message: "Connection refused" });
|
||||||
|
}
|
||||||
|
if (errorCode === "ENOTFOUND" || errorMessage.includes("ENOTFOUND")) {
|
||||||
|
throw new AcmeDnsFailureError({ message: "Hostname could not be resolved (DNS failure)" });
|
||||||
|
}
|
||||||
|
if (errorCode === "ECONNRESET" || errorMessage.includes("ECONNRESET")) {
|
||||||
|
throw new AcmeConnectionError({ message: "Connection reset by peer" });
|
||||||
|
}
|
||||||
|
if (errorCode === "ECONNABORTED" || errorMessage.includes("timeout")) {
|
||||||
|
logger.error(exp, "Connection timed out while validating ACME challenge response");
|
||||||
|
throw new AcmeConnectionError({ message: "Connection timed out" });
|
||||||
|
}
|
||||||
|
logger.error(exp, "Unknown error validating ACME challenge response");
|
||||||
|
throw new AcmeServerInternalError({ message: "Unknown error validating ACME challenge response" });
|
||||||
}
|
}
|
||||||
if (errorCode === "ENOTFOUND" || errorMessage.includes("ENOTFOUND")) {
|
if (exp instanceof Error) {
|
||||||
throw new AcmeDnsFailureError({ message: "Hostname could not be resolved (DNS failure)" });
|
logger.error(exp, "Error validating ACME challenge response");
|
||||||
}
|
throw exp;
|
||||||
if (errorCode === "ECONNRESET" || errorMessage.includes("ECONNRESET")) {
|
|
||||||
throw new AcmeConnectionError({ message: "Connection reset by peer" });
|
|
||||||
}
|
|
||||||
if (errorCode === "ECONNABORTED" || errorMessage.includes("timeout")) {
|
|
||||||
logger.error(exp, "Connection timed out while validating ACME challenge response");
|
|
||||||
throw new AcmeConnectionError({ message: "Connection timed out" });
|
|
||||||
}
|
}
|
||||||
logger.error(exp, "Unknown error validating ACME challenge response");
|
logger.error(exp, "Unknown error validating ACME challenge response");
|
||||||
throw new AcmeServerInternalError({ message: "Unknown error validating ACME challenge response" });
|
throw new AcmeServerInternalError({ message: "Unknown error validating ACME challenge response" });
|
||||||
|
} catch (outterExp) {
|
||||||
|
await auditLogService.createAuditLog({
|
||||||
|
projectId: challenge.auth.account.project.id,
|
||||||
|
actor: {
|
||||||
|
type: ActorType.ACME_ACCOUNT,
|
||||||
|
metadata: {
|
||||||
|
profileId: challenge.auth.account.profileId,
|
||||||
|
accountId: challenge.auth.account.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
type: finalAttempt ? EventType.FAIL_ACME_CHALLENGE : EventType.ATTEMPT_ACME_CHALLENGE,
|
||||||
|
metadata: {
|
||||||
|
challengeId,
|
||||||
|
type: challenge.type as AcmeChallengeType,
|
||||||
|
retryCount,
|
||||||
|
errorMessage: exp instanceof Error ? exp.message : "Unknown error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
throw outterExp;
|
||||||
}
|
}
|
||||||
if (exp instanceof Error) {
|
|
||||||
logger.error(exp, "Error validating ACME challenge response");
|
|
||||||
throw exp;
|
|
||||||
}
|
|
||||||
logger.error(exp, "Unknown error validating ACME challenge response");
|
|
||||||
throw new AcmeServerInternalError({ message: "Unknown error validating ACME challenge response" });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user