mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 23:47:06 +00:00
feat: kmip poc done
This commit is contained in:
@@ -153,7 +153,55 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/delete",
|
||||
url: "/get-attributes",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "KMIP endpoint for getting attributes of managed object",
|
||||
body: z.object({
|
||||
id: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
id: z.string(),
|
||||
algorithm: z.string(),
|
||||
isActive: z.boolean(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const object = await server.services.kmipOperation.getAttributes({
|
||||
...req.kmipUser,
|
||||
id: req.body.id
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.kmipUser.projectId,
|
||||
actor: {
|
||||
type: ActorType.KMIP_CLIENT,
|
||||
metadata: {
|
||||
clientId: req.kmipUser.clientId,
|
||||
name: req.kmipUser.name
|
||||
}
|
||||
},
|
||||
event: {
|
||||
type: EventType.KMIP_OPERATION_GET_ATTRIBUTES,
|
||||
metadata: {
|
||||
id: object.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return object;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/destroy",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
@@ -194,4 +242,193 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
|
||||
return object;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/activate",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "KMIP endpoint for activating managed object",
|
||||
body: z.object({
|
||||
id: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
id: z.string(),
|
||||
isActive: z.boolean()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const object = await server.services.kmipOperation.activate({
|
||||
...req.kmipUser,
|
||||
id: req.body.id
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.kmipUser.projectId,
|
||||
actor: {
|
||||
type: ActorType.KMIP_CLIENT,
|
||||
metadata: {
|
||||
clientId: req.kmipUser.clientId,
|
||||
name: req.kmipUser.name
|
||||
}
|
||||
},
|
||||
event: {
|
||||
type: EventType.KMIP_OPERATION_ACTIVATE,
|
||||
metadata: {
|
||||
id: object.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return object;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/revoke",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "KMIP endpoint for revoking managed object",
|
||||
body: z.object({
|
||||
id: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
id: z.string(),
|
||||
updatedAt: z.date()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const object = await server.services.kmipOperation.revoke({
|
||||
...req.kmipUser,
|
||||
id: req.body.id
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.kmipUser.projectId,
|
||||
actor: {
|
||||
type: ActorType.KMIP_CLIENT,
|
||||
metadata: {
|
||||
clientId: req.kmipUser.clientId,
|
||||
name: req.kmipUser.name
|
||||
}
|
||||
},
|
||||
event: {
|
||||
type: EventType.KMIP_OPERATION_REVOKE,
|
||||
metadata: {
|
||||
id: object.id
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return object;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/locate",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "KMIP endpoint for locating managed objects",
|
||||
response: {
|
||||
200: z.object({
|
||||
objects: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
isActive: z.boolean(),
|
||||
algorithm: z.string(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const objects = await server.services.kmipOperation.locate({
|
||||
...req.kmipUser
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.kmipUser.projectId,
|
||||
actor: {
|
||||
type: ActorType.KMIP_CLIENT,
|
||||
metadata: {
|
||||
clientId: req.kmipUser.clientId,
|
||||
name: req.kmipUser.name
|
||||
}
|
||||
},
|
||||
event: {
|
||||
type: EventType.KMIP_OPERATION_LOCATE,
|
||||
metadata: {
|
||||
ids: objects.map((obj) => obj.id)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
objects
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/register",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "KMIP endpoint for registering managed object",
|
||||
body: z.object({
|
||||
key: z.string(),
|
||||
name: z.string(),
|
||||
algorithm: z.nativeEnum(SymmetricEncryption)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
id: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const object = await server.services.kmipOperation.register({
|
||||
...req.kmipUser,
|
||||
...req.body
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId: req.kmipUser.projectId,
|
||||
actor: {
|
||||
type: ActorType.KMIP_CLIENT,
|
||||
metadata: {
|
||||
clientId: req.kmipUser.clientId,
|
||||
name: req.kmipUser.name
|
||||
}
|
||||
},
|
||||
event: {
|
||||
type: EventType.KMIP_OPERATION_REGISTER,
|
||||
metadata: {
|
||||
id: object.id,
|
||||
algorithm: req.body.algorithm,
|
||||
name: object.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return object;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -269,7 +269,12 @@ export enum EventType {
|
||||
CREATE_KMIP_CLIENT_CERTIFICATE = "create-kmip-client-certificate",
|
||||
KMIP_OPERATION_CREATE = "kmip-operation-create",
|
||||
KMIP_OPERATION_GET = "kmip-operation-get",
|
||||
KMIP_OPERATION_DELETE = "kmip-operation-delete"
|
||||
KMIP_OPERATION_DELETE = "kmip-operation-delete",
|
||||
KMIP_OPERATION_GET_ATTRIBUTES = "kmip-operation-get-attributes",
|
||||
KMIP_OPERATION_ACTIVATE = "kmip-operation-activate",
|
||||
KMIP_OPERATION_REVOKE = "kmip-operation-revoke",
|
||||
KMIP_OPERATION_LOCATE = "kmip-operation-locate",
|
||||
KMIP_OPERATION_REGISTER = "kmip-operation-register"
|
||||
}
|
||||
|
||||
interface UserActorMetadata {
|
||||
@@ -2165,6 +2170,43 @@ interface KmipOperationCreateEvent {
|
||||
};
|
||||
}
|
||||
|
||||
interface KmipOperationGetAttributesEvent {
|
||||
type: EventType.KMIP_OPERATION_GET_ATTRIBUTES;
|
||||
metadata: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface KmipOperationActivateEvent {
|
||||
type: EventType.KMIP_OPERATION_ACTIVATE;
|
||||
metadata: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface KmipOperationRevokeEvent {
|
||||
type: EventType.KMIP_OPERATION_REVOKE;
|
||||
metadata: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface KmipOperationLocateEvent {
|
||||
type: EventType.KMIP_OPERATION_LOCATE;
|
||||
metadata: {
|
||||
ids: string[];
|
||||
};
|
||||
}
|
||||
|
||||
interface KmipOperationRegisterEvent {
|
||||
type: EventType.KMIP_OPERATION_REGISTER;
|
||||
metadata: {
|
||||
id: string;
|
||||
algorithm: string;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type Event =
|
||||
| GetSecretsEvent
|
||||
| GetSecretEvent
|
||||
@@ -2364,4 +2406,9 @@ export type Event =
|
||||
| CreateKmipClientCertificateEvent
|
||||
| KmipOperationGetEvent
|
||||
| KmipOperationDeleteEvent
|
||||
| KmipOperationCreateEvent;
|
||||
| KmipOperationCreateEvent
|
||||
| KmipOperationGetAttributesEvent
|
||||
| KmipOperationActivateEvent
|
||||
| KmipOperationRevokeEvent
|
||||
| KmipOperationLocateEvent
|
||||
| KmipOperationRegisterEvent;
|
||||
|
||||
@@ -3,5 +3,9 @@ export enum KmipPermission {
|
||||
Locate = "locate",
|
||||
Check = "check",
|
||||
Get = "get",
|
||||
Delete = "delete"
|
||||
GetAttributes = "get-attributes",
|
||||
Activate = "activate",
|
||||
Revoke = "revoke",
|
||||
Delete = "delete",
|
||||
Register = "register"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,15 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
|
||||
import { TKmipClientDALFactory } from "./kmip-client-dal";
|
||||
import { KmipPermission } from "./kmip-enum";
|
||||
import { TKmipCreateDTO, TKmipDeleteDTO, TKmipGetDTO } from "./kmip-types";
|
||||
import {
|
||||
TKmipCreateDTO,
|
||||
TKmipDeleteDTO,
|
||||
TKmipGetAttributesDTO,
|
||||
TKmipGetDTO,
|
||||
TKmipLocateDTO,
|
||||
TKmipRegisterDTO,
|
||||
TKmipRevokeDTO
|
||||
} from "./kmip-types";
|
||||
|
||||
type TKmipOperationServiceFactoryDep = {
|
||||
kmsService: TKmsServiceFactory;
|
||||
@@ -143,13 +151,211 @@ export const kmipOperationServiceFactory = ({
|
||||
return {
|
||||
id: key.id,
|
||||
value: kmsKey.toString("base64"),
|
||||
algorithm: completeKeyDetails.internalKms.encryptionAlgorithm
|
||||
algorithm: completeKeyDetails.internalKms.encryptionAlgorithm,
|
||||
isActive: !key.isDisabled,
|
||||
createdAt: key.createdAt,
|
||||
updatedAt: key.updatedAt
|
||||
};
|
||||
};
|
||||
|
||||
const activate = async ({ projectId: preSplitProjectId, id, clientId }: TKmipGetDTO) => {
|
||||
let projectId = preSplitProjectId;
|
||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
||||
|
||||
if (cmekProjectFromSplit) {
|
||||
projectId = cmekProjectFromSplit.id;
|
||||
}
|
||||
|
||||
const kmipClient = await kmipClientDAL.findOne({
|
||||
id: clientId,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!kmipClient.permissions?.includes(KmipPermission.Activate)) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Client does not have sufficient permission to perform KMIP activate"
|
||||
});
|
||||
}
|
||||
|
||||
const key = await kmsDAL.findOne({
|
||||
id,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!key) {
|
||||
throw new NotFoundError({ message: `Key with ID ${id} not found` });
|
||||
}
|
||||
|
||||
return {
|
||||
id: key.id,
|
||||
isActive: !key.isDisabled
|
||||
};
|
||||
};
|
||||
|
||||
const revoke = async ({ projectId: preSplitProjectId, id, clientId }: TKmipRevokeDTO) => {
|
||||
let projectId = preSplitProjectId;
|
||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
||||
|
||||
if (cmekProjectFromSplit) {
|
||||
projectId = cmekProjectFromSplit.id;
|
||||
}
|
||||
|
||||
const kmipClient = await kmipClientDAL.findOne({
|
||||
id: clientId,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!kmipClient.permissions?.includes(KmipPermission.Revoke)) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Client does not have sufficient permission to perform KMIP revoke"
|
||||
});
|
||||
}
|
||||
|
||||
const key = await kmsDAL.findOne({
|
||||
id,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!key) {
|
||||
throw new NotFoundError({ message: `Key with ID ${id} not found` });
|
||||
}
|
||||
|
||||
if (key.isReserved) {
|
||||
throw new BadRequestError({ message: "Cannot revoke reserved keys" });
|
||||
}
|
||||
|
||||
const completeKeyDetails = await kmsDAL.findByIdWithAssociatedKms(id);
|
||||
|
||||
if (!completeKeyDetails.internalKms) {
|
||||
throw new BadRequestError({
|
||||
message: "Cannot revoke external keys"
|
||||
});
|
||||
}
|
||||
|
||||
const revokedKey = await kmsDAL.updateById(key.id, {
|
||||
isDisabled: true
|
||||
});
|
||||
|
||||
return {
|
||||
id: key.id,
|
||||
updatedAt: revokedKey.updatedAt
|
||||
};
|
||||
};
|
||||
|
||||
const getAttributes = async ({ projectId: preSplitProjectId, id, clientId }: TKmipGetAttributesDTO) => {
|
||||
let projectId = preSplitProjectId;
|
||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
||||
|
||||
if (cmekProjectFromSplit) {
|
||||
projectId = cmekProjectFromSplit.id;
|
||||
}
|
||||
|
||||
const kmipClient = await kmipClientDAL.findOne({
|
||||
id: clientId,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!kmipClient.permissions?.includes(KmipPermission.GetAttributes)) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Client does not have sufficient permission to perform KMIP get attributes"
|
||||
});
|
||||
}
|
||||
|
||||
const key = await kmsDAL.findOne({
|
||||
id,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!key) {
|
||||
throw new NotFoundError({ message: `Key with ID ${id} not found` });
|
||||
}
|
||||
|
||||
if (key.isReserved) {
|
||||
throw new BadRequestError({ message: "Cannot get reserved keys" });
|
||||
}
|
||||
|
||||
const completeKeyDetails = await kmsDAL.findByIdWithAssociatedKms(id);
|
||||
|
||||
if (!completeKeyDetails.internalKms) {
|
||||
throw new BadRequestError({
|
||||
message: "Cannot get external keys"
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: key.id,
|
||||
algorithm: completeKeyDetails.internalKms.encryptionAlgorithm,
|
||||
isActive: !key.isDisabled,
|
||||
createdAt: key.createdAt,
|
||||
updatedAt: key.updatedAt
|
||||
};
|
||||
};
|
||||
|
||||
const locate = async ({ projectId: preSplitProjectId, clientId }: TKmipLocateDTO) => {
|
||||
let projectId = preSplitProjectId;
|
||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
||||
|
||||
if (cmekProjectFromSplit) {
|
||||
projectId = cmekProjectFromSplit.id;
|
||||
}
|
||||
|
||||
const kmipClient = await kmipClientDAL.findOne({
|
||||
id: clientId,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!kmipClient.permissions?.includes(KmipPermission.Locate)) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Client does not have sufficient permission to perform KMIP locate"
|
||||
});
|
||||
}
|
||||
|
||||
const keys = await kmsDAL.findProjectCmeks(projectId);
|
||||
|
||||
return keys;
|
||||
};
|
||||
|
||||
const register = async ({ projectId: preSplitProjectId, clientId, key, algorithm, name }: TKmipRegisterDTO) => {
|
||||
let projectId = preSplitProjectId;
|
||||
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
|
||||
|
||||
if (cmekProjectFromSplit) {
|
||||
projectId = cmekProjectFromSplit.id;
|
||||
}
|
||||
|
||||
const kmipClient = await kmipClientDAL.findOne({
|
||||
id: clientId,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (!kmipClient.permissions?.includes(KmipPermission.Register)) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Client does not have sufficient permission to perform KMIP register"
|
||||
});
|
||||
}
|
||||
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
const kmsKey = await kmsService.importKeyMaterial({
|
||||
name,
|
||||
key: Buffer.from(key, "base64"),
|
||||
algorithm,
|
||||
isReserved: false,
|
||||
projectId,
|
||||
orgId: project.orgId
|
||||
});
|
||||
|
||||
return kmsKey;
|
||||
};
|
||||
|
||||
return {
|
||||
create,
|
||||
get,
|
||||
deleteOp
|
||||
activate,
|
||||
getAttributes,
|
||||
deleteOp,
|
||||
revoke,
|
||||
locate,
|
||||
register
|
||||
};
|
||||
};
|
||||
|
||||
@@ -56,6 +56,26 @@ export type TKmipGetDTO = {
|
||||
id: string;
|
||||
} & KmipOperationBaseDTO;
|
||||
|
||||
export type TKmipGetAttributesDTO = {
|
||||
id: string;
|
||||
} & KmipOperationBaseDTO;
|
||||
|
||||
export type TKmipDeleteDTO = {
|
||||
id: string;
|
||||
} & KmipOperationBaseDTO;
|
||||
|
||||
export type TKmipActivateDTO = {
|
||||
id: string;
|
||||
} & KmipOperationBaseDTO;
|
||||
|
||||
export type TKmipRevokeDTO = {
|
||||
id: string;
|
||||
} & KmipOperationBaseDTO;
|
||||
|
||||
export type TKmipLocateDTO = KmipOperationBaseDTO;
|
||||
|
||||
export type TKmipRegisterDTO = {
|
||||
name: string;
|
||||
key: string;
|
||||
algorithm: SymmetricEncryption;
|
||||
} & KmipOperationBaseDTO;
|
||||
|
||||
Reference in New Issue
Block a user