misc: finalize KMIP management

This commit is contained in:
Sheen Capadngan
2025-02-18 02:22:59 +08:00
parent 603b740bbe
commit 943c2b0e69
20 changed files with 490 additions and 317 deletions
@@ -1,5 +1,3 @@
import crypto from "crypto";
import jwt, { JwtPayload } from "jsonwebtoken";
import z from "zod";
import { KmsKeysSchema } from "@app/db/schemas";
@@ -7,21 +5,17 @@ import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { writeLimit } from "@app/server/config/rateLimiter";
import { ActorType } from "@app/services/auth/auth-type";
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
export const registerKmipOperationRouter = async (server: FastifyZodProvider) => {
server.decorateRequest("kmipUser", null);
server.addHook("preHandler", async (req) => {
const token = req.headers["x-kmip-jwt"] as string;
const serverCertSerialNumber = req.headers["x-server-certificate-serial-number"] as string;
if (!jwt) {
throw new ForbiddenRequestError({
message: "Missing KMIP JWT"
});
}
server.addHook("onRequest", async (req) => {
const clientId = req.headers["x-kmip-client-id"] as string;
const projectId = req.headers["x-kmip-project-id"] as string;
const clientCertSerialNumber = req.headers["x-kmip-client-certificate-serial-number"] as string;
const serverCertSerialNumber = req.headers["x-kmip-server-certificate-serial-number"] as string;
if (!serverCertSerialNumber) {
throw new ForbiddenRequestError({
@@ -29,25 +23,28 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
});
}
const serverCert = await server.services.kmip.getServerCertificateBySerialNumber(serverCertSerialNumber);
if (!clientCertSerialNumber) {
throw new ForbiddenRequestError({
message: "Missing client certificate serial number from request"
});
}
if (!clientId) {
throw new ForbiddenRequestError({
message: "Missing client ID from request"
});
}
if (!projectId) {
throw new ForbiddenRequestError({
message: "Missing project ID from request"
});
}
// TODO: assert that server certificate used is not revoked
// TODO: assert that client certificate used is not revoked
const publicKey = crypto.createPublicKey({
key: serverCert.publicKey,
format: "pem",
type: [CertKeyAlgorithm.ECDSA_P256, CertKeyAlgorithm.ECDSA_P384].includes(serverCert.keyAlgorithm)
? "spki"
: "pkcs1"
});
const decodedToken = jwt.verify(token, publicKey) as JwtPayload & { projectId: string; clientId: string };
const kmipClient = await server.store.kmipClient.findOne({
id: decodedToken.clientId,
projectId: decodedToken.projectId
});
const kmipClient = await server.store.kmipClient.findByProjectAndClientId(projectId, clientId);
if (!kmipClient) {
throw new NotFoundError({
@@ -55,9 +52,15 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
});
}
if (kmipClient.orgId !== req.permission.orgId) {
throw new ForbiddenRequestError({
message: "Client specified in the request does not belong in the organization"
});
}
req.kmipUser = {
projectId: decodedToken.projectId,
clientId: decodedToken.clientId,
projectId,
clientId,
name: kmipClient.name
};
});
@@ -77,9 +80,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
200: KmsKeysSchema
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const object = await server.services.kmipOperation.create({
...req.kmipUser,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
algorithm: req.body.algorithm
});
@@ -124,9 +132,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
})
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const object = await server.services.kmipOperation.get({
...req.kmipUser,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.body.id
});
@@ -172,9 +185,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
})
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const object = await server.services.kmipOperation.getAttributes({
...req.kmipUser,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.body.id
});
@@ -216,9 +234,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
})
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const object = await server.services.kmipOperation.deleteOp({
...req.kmipUser,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.body.id
});
@@ -261,9 +284,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
})
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const object = await server.services.kmipOperation.activate({
...req.kmipUser,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.body.id
});
@@ -306,9 +334,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
})
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const object = await server.services.kmipOperation.revoke({
...req.kmipUser,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.body.id
});
@@ -356,9 +389,14 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
})
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const objects = await server.services.kmipOperation.locate({
...req.kmipUser
...req.kmipUser,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
@@ -403,10 +441,15 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) =>
})
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const object = await server.services.kmipOperation.register({
...req.kmipUser,
...req.body
...req.body,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
+52 -9
View File
@@ -306,13 +306,26 @@ export const registerKmipRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
return server.services.kmip.setupOrgKmip({
const chains = await server.services.kmip.setupOrgKmip({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.SETUP_KMIP,
metadata: {
keyAlgorithm: req.body.caKeyAlgorithm
}
}
});
return chains;
}
});
@@ -332,46 +345,76 @@ export const registerKmipRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
return server.services.kmip.getOrgKmip({
const kmip = await server.services.kmip.getOrgKmip({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.GET_KMIP,
metadata: {
id: kmip.id
}
}
});
return kmip;
}
});
server.route({
method: "POST",
url: "/server-certificates",
url: "/server-registration",
config: {
rateLimit: writeLimit
},
schema: {
body: z.object({
commonName: z.string().trim().min(1),
altNames: validateAltNamesField,
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm),
hostnamesOrIps: validateAltNamesField,
commonName: z.string().trim().min(1).optional(),
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm).optional().default(CertKeyAlgorithm.RSA_2048),
ttl: z.string().refine((val) => ms(val) > 0, "TTL must be a positive number")
}),
response: {
200: z.object({
serialNumber: z.string(),
clientCertificateChain: z.string(),
certificateChain: z.string(),
certificate: z.string(),
privateKey: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
return server.services.kmip.generateOrgKmipServerCertificate({
const configs = await server.services.kmip.registerServer({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.REGISTER_KMIP_SERVER,
metadata: {
serverCertificateSerialNumber: configs.serverCertificateSerialNumber,
hostnamesOrIps: req.body.hostnamesOrIps,
commonName: req.body.commonName ?? "kmip-server",
keyAlgorithm: req.body.keyAlgorithm,
ttl: req.body.ttl
}
}
});
return configs;
}
});
};