Add permissioning to SSH, add publicKey return for SSH CA, polish

This commit is contained in:
Tuan Dang
2024-12-03 17:38:23 -08:00
parent 4fc8c509ac
commit 07d9398aad
25 changed files with 397 additions and 106 deletions
@@ -29,7 +29,9 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
ca: sanitizedSshCa
ca: sanitizedSshCa.extend({
publicKey: z.string()
})
})
}
},
@@ -74,7 +76,9 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
ca: sanitizedSshCa
ca: sanitizedSshCa.extend({
publicKey: z.string()
})
})
}
},
@@ -109,7 +113,7 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
method: "PATCH",
url: "/:sshCaId",
config: {
rateLimit: readLimit
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
@@ -118,6 +122,7 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
sshCaId: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.UPDATE.sshCaId)
}),
body: z.object({
friendlyName: z.string().optional().describe(SSH_CERTIFICATE_AUTHORITIES.UPDATE.friendlyName),
status: z
.enum([SshCaStatus.ACTIVE, SshCaStatus.DISABLED])
.optional()
@@ -125,7 +130,9 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
ca: sanitizedSshCa
ca: sanitizedSshCa.extend({
publicKey: z.string()
})
})
}
},
@@ -1,3 +1,4 @@
import slugify from "@sindresorhus/slugify";
import ms from "ms";
import { z } from "zod";
@@ -61,7 +62,14 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
schema: {
body: z.object({
sshCaId: z.string().describe(SSH_CERTIFICATE_TEMPLATES.CREATE.sshCaId),
name: z.string().min(1).describe(SSH_CERTIFICATE_TEMPLATES.CREATE.name),
name: z
.string()
.min(1)
.max(36)
.refine((v) => slugify(v) === v, {
message: "Name must be a valid slug"
})
.describe(SSH_CERTIFICATE_TEMPLATES.CREATE.name),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
@@ -128,7 +136,15 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
},
schema: {
body: z.object({
name: z.string().min(1).optional().describe(SSH_CERTIFICATE_TEMPLATES.UPDATE.name),
name: z
.string()
.min(1)
.max(36)
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
})
.optional()
.describe(SSH_CERTIFICATE_TEMPLATES.UPDATE.name),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
+34 -19
View File
@@ -3,7 +3,7 @@ import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
import { CERTIFICATE_AUTHORITIES, CERTIFICATE_TEMPLATES } from "@app/lib/api-docs"; // TODO: update to SSH CA
import { SSH_CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
import { writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -20,21 +20,27 @@ export const registerSshRouter = async (server: FastifyZodProvider) => {
schema: {
description: "Sign SSH public key",
body: z.object({
name: z.string(), // name of SSH certificate template
publicKey: z.string(),
certType: z.nativeEnum(SshCertType).default(SshCertType.USER),
principals: z.array(z.string().transform((val) => val.trim())).nonempty("Principals array must not be empty"),
templateName: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.templateName),
publicKey: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.publicKey),
certType: z
.nativeEnum(SshCertType)
.default(SshCertType.USER)
.describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.certType),
principals: z
.array(z.string().transform((val) => val.trim()))
.nonempty("Principals array must not be empty")
.describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.principals),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.optional()
.describe(CERTIFICATE_TEMPLATES.CREATE.ttl),
keyId: z.string().optional()
.describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.ttl),
keyId: z.string().trim().optional().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.keyId)
}),
response: {
200: z.object({
serialNumber: z.string(),
signedKey: z.string()
serialNumber: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.serialNumber),
signedKey: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.signedKey)
})
}
},
@@ -80,26 +86,35 @@ export const registerSshRouter = async (server: FastifyZodProvider) => {
schema: {
description: "Issue SSH credentials (certificate + key)",
body: z.object({
name: z.string(), // name of SSH certificate template
templateName: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.templateName),
keyAlgorithm: z
.nativeEnum(CertKeyAlgorithm)
.default(CertKeyAlgorithm.RSA_2048)
.describe(CERTIFICATE_AUTHORITIES.CREATE.keyAlgorithm),
certType: z.nativeEnum(SshCertType).default(SshCertType.USER),
principals: z.array(z.string().transform((val) => val.trim())).nonempty("Principals array must not be empty"),
.describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.keyAlgorithm),
certType: z
.nativeEnum(SshCertType)
.default(SshCertType.USER)
.describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.certType),
principals: z
.array(z.string().transform((val) => val.trim()))
.nonempty("Principals array must not be empty")
.describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.principals),
ttl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.optional()
.describe(CERTIFICATE_TEMPLATES.CREATE.ttl),
keyId: z.string().optional()
.describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.ttl),
keyId: z.string().trim().optional()
}),
response: {
200: z.object({
serialNumber: z.string(),
signedKey: z.string(),
privateKey: z.string(),
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm)
serialNumber: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.serialNumber),
signedKey: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.signedKey),
privateKey: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.privateKey),
publicKey: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.publicKey),
keyAlgorithm: z
.nativeEnum(CertKeyAlgorithm)
.describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.keyAlgorithm)
})
}
},