mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Replace custom pkcs7 fns with module
This commit is contained in:
@@ -692,7 +692,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate,
|
certificate: certificate.toString("pem"),
|
||||||
certificateChain,
|
certificateChain,
|
||||||
issuingCaCertificate,
|
issuingCaCertificate,
|
||||||
serialNumber
|
serialNumber
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate,
|
certificate: certificate.toString("pem"),
|
||||||
certificateChain,
|
certificateChain,
|
||||||
issuingCaCertificate,
|
issuingCaCertificate,
|
||||||
serialNumber
|
serialNumber
|
||||||
|
|||||||
@@ -1553,8 +1553,10 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
certificate: leafCert.toString("pem"),
|
certificate: leafCert,
|
||||||
rawCertificate: leafCert.rawData,
|
// certificate: leafCert.toString("pem"),
|
||||||
|
// certificateObj: leafCert,
|
||||||
|
// rawCertificate: leafCert.rawData,
|
||||||
certificateChain: `${issuingCaCertificate}\n${caCertChain}`.trim(),
|
certificateChain: `${issuingCaCertificate}\n${caCertChain}`.trim(),
|
||||||
issuingCaCertificate,
|
issuingCaCertificate,
|
||||||
serialNumber,
|
serialNumber,
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Certificate, ContentInfo, EncapsulatedContentInfo, SignedData } from "pkijs";
|
|
||||||
|
|
||||||
export const convertRawCertsToPkcs7 = (rawCertificate: ArrayBuffer[]) => {
|
|
||||||
const certs = rawCertificate.map((rawCert) => Certificate.fromBER(rawCert));
|
|
||||||
const cmsSigned = new SignedData({
|
|
||||||
encapContentInfo: new EncapsulatedContentInfo({
|
|
||||||
eContentType: "1.2.840.113549.1.7.1" // not encrypted and not compressed data
|
|
||||||
}),
|
|
||||||
certificates: certs
|
|
||||||
});
|
|
||||||
|
|
||||||
const cmsContent = new ContentInfo({
|
|
||||||
contentType: "1.2.840.113549.1.7.2", // SignedData
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
content: cmsSigned.toSchema()
|
|
||||||
});
|
|
||||||
|
|
||||||
const derBuffer = cmsContent.toSchema().toBER(false);
|
|
||||||
const base64Pkcs7 = Buffer.from(derBuffer).toString("base64");
|
|
||||||
|
|
||||||
return base64Pkcs7;
|
|
||||||
};
|
|
||||||
@@ -2,7 +2,6 @@ import * as x509 from "@peculiar/x509";
|
|||||||
|
|
||||||
import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||||
|
|
||||||
import { checkCertValidityAgainstChain, convertCertPemToRaw } from "../certificate/certificate-fns";
|
|
||||||
import { TCertificateAuthorityCertDALFactory } from "../certificate-authority/certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "../certificate-authority/certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal";
|
||||||
import { getCaCertChain, getCaCertChains } from "../certificate-authority/certificate-authority-fns";
|
import { getCaCertChain, getCaCertChains } from "../certificate-authority/certificate-authority-fns";
|
||||||
@@ -11,7 +10,6 @@ import { TCertificateTemplateDALFactory } from "../certificate-template/certific
|
|||||||
import { TCertificateTemplateServiceFactory } from "../certificate-template/certificate-template-service";
|
import { TCertificateTemplateServiceFactory } from "../certificate-template/certificate-template-service";
|
||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { TProjectDALFactory } from "../project/project-dal";
|
import { TProjectDALFactory } from "../project/project-dal";
|
||||||
import { convertRawCertsToPkcs7 } from "./certificate-est-fns";
|
|
||||||
|
|
||||||
type TCertificateEstServiceFactoryDep = {
|
type TCertificateEstServiceFactoryDep = {
|
||||||
certificateAuthorityService: Pick<TCertificateAuthorityServiceFactory, "signCertFromCa">;
|
certificateAuthorityService: Pick<TCertificateAuthorityServiceFactory, "signCertFromCa">;
|
||||||
@@ -60,7 +58,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g
|
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g
|
||||||
)?.[0];
|
)?.[0];
|
||||||
|
|
||||||
if (!sslClientCert || !leafCertificate) {
|
if (!leafCertificate) {
|
||||||
throw new UnauthorizedError({ message: "Missing client certificate" });
|
throw new UnauthorizedError({ message: "Missing client certificate" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,43 +80,27 @@ export const certificateEstServiceFactory = ({
|
|||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const parsedChains = caCertChains
|
const caChainBuilders = caCertChains.map((chain) => {
|
||||||
// we need the full chain from the CA certificate to the root
|
const caCert = new x509.X509Certificate(chain.certificate);
|
||||||
.map((chain) => chain.certificate + chain.certificateChain)
|
const caChain =
|
||||||
.map(
|
chain.certificateChain
|
||||||
(certificateChain) =>
|
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
|
||||||
certificateChain.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)?.map((certEntry) => {
|
?.map((c) => new x509.X509Certificate(c)) || [];
|
||||||
const processedBody = certEntry
|
return new x509.X509ChainBuilder({
|
||||||
.replace("-----BEGIN CERTIFICATE-----", "")
|
certificates: [caCert, ...caChain]
|
||||||
.replace("-----END CERTIFICATE-----", "")
|
|
||||||
.replace(/\n/g, "")
|
|
||||||
.replace(/ /g, "")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
const certificateBuffer = Buffer.from(processedBody, "base64");
|
|
||||||
return new x509.X509Certificate(certificateBuffer);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!parsedChains || !parsedChains.length) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Error parsing CA chain"
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
const certValidityAgainstChains = await Promise.all(
|
const verifiedChains = await Promise.all(
|
||||||
parsedChains.map(async (chain) => {
|
caChainBuilders.map(async (caChainBuilder) => {
|
||||||
if (!chain) {
|
const chainItems = await caChainBuilder.build(cert);
|
||||||
return false;
|
return chainItems.length === caChainBuilder.certificates.length + 1;
|
||||||
}
|
|
||||||
|
|
||||||
return checkCertValidityAgainstChain(cert, chain);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (certValidityAgainstChains.every((isCertValid) => !isCertValid)) {
|
if (!verifiedChains.some(Boolean)) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Invalid client certificate"
|
message: "Invalid client certificate: unable to build a valid certificate chain"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,13 +132,14 @@ export const certificateEstServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { rawCertificate } = await certificateAuthorityService.signCertFromCa({
|
const { certificate } = await certificateAuthorityService.signCertFromCa({
|
||||||
isInternal: true,
|
isInternal: true,
|
||||||
certificateTemplateId,
|
certificateTemplateId,
|
||||||
csr
|
csr
|
||||||
});
|
});
|
||||||
|
|
||||||
return convertRawCertsToPkcs7([rawCertificate]);
|
const certs = new x509.X509Certificates([certificate]);
|
||||||
|
return certs.export("base64");
|
||||||
};
|
};
|
||||||
|
|
||||||
const simpleEnroll = async ({
|
const simpleEnroll = async ({
|
||||||
@@ -182,12 +165,24 @@ export const certificateEstServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const caCerts = estConfig.caChain
|
||||||
|
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
|
||||||
|
?.map((cert) => {
|
||||||
|
return new x509.X509Certificate(cert);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!caCerts) throw new BadRequestError({ message: "Failed to parse certificate chain" });
|
||||||
|
|
||||||
|
const caChain = new x509.X509ChainBuilder({
|
||||||
|
certificates: caCerts
|
||||||
|
});
|
||||||
|
|
||||||
const leafCertificate = decodeURIComponent(sslClientCert).match(
|
const leafCertificate = decodeURIComponent(sslClientCert).match(
|
||||||
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g
|
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g
|
||||||
)?.[0];
|
)?.[0];
|
||||||
|
|
||||||
if (!sslClientCert || !leafCertificate) {
|
if (!leafCertificate) {
|
||||||
throw new UnauthorizedError({ message: "Missing client certificate" });
|
throw new BadRequestError({ message: "Missing client certificate" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientCertBody = leafCertificate
|
const clientCertBody = leafCertificate
|
||||||
@@ -197,41 +192,25 @@ export const certificateEstServiceFactory = ({
|
|||||||
.replace(/ /g, "")
|
.replace(/ /g, "")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const chainCerts = estConfig.caChain
|
const certObj = new x509.X509Certificate(clientCertBody);
|
||||||
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
|
const chainItems = await caChain.build(certObj);
|
||||||
?.map((cert) => {
|
|
||||||
const processedBody = cert
|
|
||||||
.replace("-----BEGIN CERTIFICATE-----", "")
|
|
||||||
.replace("-----END CERTIFICATE-----", "")
|
|
||||||
.replace(/\n/g, "")
|
|
||||||
.replace(/ /g, "")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
const certificateBuffer = Buffer.from(processedBody, "base64");
|
if (chainItems.length !== caCerts.length + 1) throw new BadRequestError({ message: "Invalid certificate chain" });
|
||||||
return new x509.X509Certificate(certificateBuffer);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!chainCerts) {
|
const { certificate } = await certificateAuthorityService.signCertFromCa({
|
||||||
throw new BadRequestError({ message: "Failed to parse certificate chain" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const cert = new x509.X509Certificate(clientCertBody);
|
|
||||||
|
|
||||||
if (!(await checkCertValidityAgainstChain(cert, chainCerts))) {
|
|
||||||
throw new UnauthorizedError({
|
|
||||||
message: "Invalid client certificate"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { rawCertificate } = await certificateAuthorityService.signCertFromCa({
|
|
||||||
isInternal: true,
|
isInternal: true,
|
||||||
certificateTemplateId,
|
certificateTemplateId,
|
||||||
csr
|
csr
|
||||||
});
|
});
|
||||||
|
|
||||||
return convertRawCertsToPkcs7([rawCertificate]);
|
const certs = new x509.X509Certificates([certificate]);
|
||||||
|
return certs.export("base64");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the CA certificate and CA certificate chain for the CA bound to
|
||||||
|
* the certificate template with id [certificateTemplateId] as part of EST protocol
|
||||||
|
*/
|
||||||
const getCaCerts = async ({ certificateTemplateId }: { certificateTemplateId: string }) => {
|
const getCaCerts = async ({ certificateTemplateId }: { certificateTemplateId: string }) => {
|
||||||
const certTemplate = await certificateTemplateDAL.findById(certificateTemplateId);
|
const certTemplate = await certificateTemplateDAL.findById(certificateTemplateId);
|
||||||
if (!certTemplate) {
|
if (!certTemplate) {
|
||||||
@@ -255,12 +234,22 @@ export const certificateEstServiceFactory = ({
|
|||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const caCertRaw = convertCertPemToRaw(caCert);
|
const certificates = caCertChain
|
||||||
const caParentsRaw = caCertChain
|
|
||||||
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
|
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
|
||||||
?.map(convertCertPemToRaw);
|
?.map((cert) => new x509.X509Certificate(cert));
|
||||||
|
|
||||||
return convertRawCertsToPkcs7([caCertRaw, ...(caParentsRaw ?? [])]);
|
if (!certificates) throw new BadRequestError({ message: "Failed to parse certificate chain" });
|
||||||
|
|
||||||
|
const chain = new x509.X509ChainBuilder({
|
||||||
|
certificates
|
||||||
|
});
|
||||||
|
|
||||||
|
const chainItems = await chain.build(new x509.X509Certificate(caCert));
|
||||||
|
|
||||||
|
if (chainItems.length !== certificates.length + 1)
|
||||||
|
throw new BadRequestError({ message: "Invalid certificate chain" });
|
||||||
|
|
||||||
|
return chainItems.export("base64");
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -24,26 +24,3 @@ export const revocationReasonToCrlCode = (crlReason: CrlReason) => {
|
|||||||
return x509.X509CrlReason.unspecified;
|
return x509.X509CrlReason.unspecified;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const convertCertPemToRaw = (certPem: string) => {
|
|
||||||
return new x509.X509Certificate(certPem).rawData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const checkCertValidityAgainstChain = async (cert: x509.X509Certificate, chainCerts: x509.X509Certificate[]) => {
|
|
||||||
let isSslClientCertValid = true;
|
|
||||||
let certToVerify = cert;
|
|
||||||
|
|
||||||
for await (const issuerCert of chainCerts) {
|
|
||||||
if (
|
|
||||||
await certToVerify.verify({
|
|
||||||
publicKey: issuerCert.publicKey
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
certToVerify = issuerCert; // Move to the next certificate in the chain
|
|
||||||
} else {
|
|
||||||
isSslClientCertValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isSslClientCertValid;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 8080:80
|
- 8080:80
|
||||||
|
- 8443:443
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro
|
- ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
|
// import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -178,6 +180,7 @@ export const CertificateTemplateEnrollmentModal = ({ popUp, handlePopUpToggle }:
|
|||||||
type={isPassphraseFocused ? "text" : "password"}
|
type={isPassphraseFocused ? "text" : "password"}
|
||||||
onFocus={() => setIsPassphraseFocused.on()}
|
onFocus={() => setIsPassphraseFocused.on()}
|
||||||
onBlur={() => setIsPassphraseFocused.off()}
|
onBlur={() => setIsPassphraseFocused.off()}
|
||||||
|
// rightIcon={<FontAwesomeIcon icon={faEyeSlash} />}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
@@ -193,7 +196,7 @@ export const CertificateTemplateEnrollmentModal = ({ popUp, handlePopUpToggle }:
|
|||||||
onCheckedChange={(value) => field.onChange(value)}
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
isChecked={field.value}
|
isChecked={field.value}
|
||||||
>
|
>
|
||||||
<p className="ml-1 w-full">Enabled</p>
|
<p className="ml-1 w-full">EST Enabled</p>
|
||||||
</Switch>
|
</Switch>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
|
|
||||||
|
large_client_header_buffers 8 128k;
|
||||||
|
client_header_buffer_size 128k;
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
proxy_set_header X-Real-RIP $remote_addr;
|
proxy_set_header X-Real-RIP $remote_addr;
|
||||||
@@ -23,6 +26,8 @@ server {
|
|||||||
proxy_set_header X-NginX-Proxy true;
|
proxy_set_header X-NginX-Proxy true;
|
||||||
|
|
||||||
proxy_set_header X-SSL-Client-Cert $ssl_client_escaped_cert;
|
proxy_set_header X-SSL-Client-Cert $ssl_client_escaped_cert;
|
||||||
|
# proxy_set_header X-SSL-Client-Cert $http_x_ssl_client_cert;
|
||||||
|
# proxy_pass_request_headers on;
|
||||||
|
|
||||||
proxy_pass http://backend:4000;
|
proxy_pass http://backend:4000;
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
|
|||||||
Reference in New Issue
Block a user