mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 01:27:31 +00:00
Allow empty CN
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
Feature: Internal CA
|
Feature: Internal CA
|
||||||
|
|
||||||
Scenario Outline: CSR with SANs only
|
Scenario: CSR with SANs only
|
||||||
Given I have an ACME cert profile as "acme_profile"
|
Given I have an ACME cert profile as "acme_profile"
|
||||||
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory"
|
When I have an ACME client connecting to "{BASE_URL}/api/v1/pki/acme/profiles/{acme_profile.id}/directory"
|
||||||
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
|
Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account
|
||||||
When I create certificate signing request as csr
|
When I create certificate signing request as csr
|
||||||
Then I add names to certificate signing request csr
|
Then I add names to certificate signing request csr
|
||||||
"""
|
"""
|
||||||
<names>
|
{}
|
||||||
"""
|
"""
|
||||||
And I add subject alternative name to certificate signing request csr
|
And I add subject alternative name to certificate signing request csr
|
||||||
"""
|
"""
|
||||||
@@ -25,8 +25,3 @@ Feature: Internal CA
|
|||||||
And the value finalized_order.body with jq ".status" should be equal to "valid"
|
And the value finalized_order.body with jq ".status" should be equal to "valid"
|
||||||
And I parse the full-chain certificate from order finalized_order as cert
|
And I parse the full-chain certificate from order finalized_order as cert
|
||||||
And the value cert with jq ".subject.common_name" should be equal to "localhost"
|
And the value cert with jq ".subject.common_name" should be equal to "localhost"
|
||||||
|
|
||||||
Examples:
|
|
||||||
| names |
|
|
||||||
| {} |
|
|
||||||
| {"COMMON_NAME": ""}
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const pkiAcmeChallengeServiceFactory = ({
|
|||||||
const challengeResponse = await axios.get<string>(challengeUrl.toString(), {
|
const challengeResponse = await axios.get<string>(challengeUrl.toString(), {
|
||||||
// In case if we override the host in the development mode, still provide the original host in the header
|
// In case if we override the host in the development mode, still provide the original host in the header
|
||||||
// to help the upstream server to validate the request
|
// to help the upstream server to validate the request
|
||||||
headers: { Host: host },
|
headers: { Host: challenge.auth.identifierValue },
|
||||||
timeout: timeoutMs,
|
timeout: timeoutMs,
|
||||||
responseType: "text",
|
responseType: "text",
|
||||||
validateStatus: () => true
|
validateStatus: () => true
|
||||||
|
|||||||
@@ -703,9 +703,6 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
|
|
||||||
// Check and validate the CSR
|
// Check and validate the CSR
|
||||||
const certificateRequest = extractCertificateRequestFromCSR(csr);
|
const certificateRequest = extractCertificateRequestFromCSR(csr);
|
||||||
if (!certificateRequest.commonName) {
|
|
||||||
throw new AcmeBadCSRError({ message: "Invalid CSR: Common name is required" });
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
certificateRequest.subjectAlternativeNames?.some(
|
certificateRequest.subjectAlternativeNames?.some(
|
||||||
(san) => san.type !== CertSubjectAlternativeNameType.DNS_NAME
|
(san) => san.type !== CertSubjectAlternativeNameType.DNS_NAME
|
||||||
@@ -721,7 +718,7 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
const csrIdentifierValues = new Set(
|
const csrIdentifierValues = new Set(
|
||||||
(certificateRequest.subjectAlternativeNames ?? [])
|
(certificateRequest.subjectAlternativeNames ?? [])
|
||||||
.map((san) => san.value.toLowerCase())
|
.map((san) => san.value.toLowerCase())
|
||||||
.concat([certificateRequest.commonName.toLowerCase()])
|
.concat(certificateRequest.commonName ? [certificateRequest.commonName.toLowerCase()] : [])
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
csrIdentifierValues.size !== orderWithAuthorizations.authorizations.length ||
|
csrIdentifierValues.size !== orderWithAuthorizations.authorizations.length ||
|
||||||
@@ -758,7 +755,8 @@ export const pkiAcmeServiceFactory = ({
|
|||||||
}
|
}
|
||||||
: // ttl is not used if notAfter is provided
|
: // ttl is not used if notAfter is provided
|
||||||
({ ttl: "0d" } as const),
|
({ ttl: "0d" } as const),
|
||||||
enrollmentType: EnrollmentType.ACME
|
enrollmentType: EnrollmentType.ACME,
|
||||||
|
allowEmptyCommonName: true
|
||||||
});
|
});
|
||||||
return { certificateId: result.certificateId };
|
return { certificateId: result.certificateId };
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-3
@@ -1577,7 +1577,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
keyUsages,
|
keyUsages,
|
||||||
extendedKeyUsages,
|
extendedKeyUsages,
|
||||||
signatureAlgorithm,
|
signatureAlgorithm,
|
||||||
keyAlgorithm
|
keyAlgorithm,
|
||||||
|
allowEmptyCommonName
|
||||||
} = dto;
|
} = dto;
|
||||||
|
|
||||||
let collectionId = pkiCollectionId;
|
let collectionId = pkiCollectionId;
|
||||||
@@ -1716,12 +1717,18 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
const csrObj = new x509.Pkcs10CertificateRequest(csr);
|
const csrObj = new x509.Pkcs10CertificateRequest(csr);
|
||||||
|
|
||||||
const dn = parseDistinguishedName(csrObj.subject);
|
const dn = parseDistinguishedName(csrObj.subject);
|
||||||
const cn = commonName || dn.commonName;
|
let cn = commonName || dn.commonName;
|
||||||
|
|
||||||
if (!cn)
|
if ((allowEmptyCommonName ?? false) && !cn) {
|
||||||
|
// Notice: for modern TLS certificates, the CN is deprecated, many ACME clients will generate CSRs with without a CN
|
||||||
|
// we allow empty CN here to support ACME clients mostly. Since it's unclear what's the side effect of
|
||||||
|
// allowing empty CN for legacy PKI code, let's only do it if a true allowEmptyCommonName value is provided.
|
||||||
|
cn = "";
|
||||||
|
} else if (!cn) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "A common name (CN) is required in the CSR or as a parameter to this endpoint"
|
message: "A common name (CN) is required in the CSR or as a parameter to this endpoint"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { caPrivateKey, caSecret } = await getCaCredentials({
|
const { caPrivateKey, caSecret } = await getCaCredentials({
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
|
|||||||
+2
@@ -164,6 +164,7 @@ export type TSignCertFromCaDTO =
|
|||||||
keyAlgorithm?: string;
|
keyAlgorithm?: string;
|
||||||
isFromProfile?: boolean;
|
isFromProfile?: boolean;
|
||||||
profileId?: string;
|
profileId?: string;
|
||||||
|
allowEmptyCommonName?: boolean;
|
||||||
}
|
}
|
||||||
| ({
|
| ({
|
||||||
isInternal: false;
|
isInternal: false;
|
||||||
@@ -183,6 +184,7 @@ export type TSignCertFromCaDTO =
|
|||||||
keyAlgorithm?: string;
|
keyAlgorithm?: string;
|
||||||
isFromProfile?: boolean;
|
isFromProfile?: boolean;
|
||||||
profileId?: string;
|
profileId?: string;
|
||||||
|
allowEmptyCommonName?: boolean;
|
||||||
} & Omit<TProjectPermission, "projectId">);
|
} & Omit<TProjectPermission, "projectId">);
|
||||||
|
|
||||||
export type TGetCaCertificateTemplatesDTO = {
|
export type TGetCaCertificateTemplatesDTO = {
|
||||||
|
|||||||
@@ -511,7 +511,8 @@ export const certificateV3ServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
enrollmentType,
|
enrollmentType,
|
||||||
removeRootsFromChain
|
removeRootsFromChain,
|
||||||
|
allowEmptyCommonName
|
||||||
}: TSignCertificateFromProfileDTO): Promise<Omit<TCertificateFromProfileResponse, "privateKey">> => {
|
}: TSignCertificateFromProfileDTO): Promise<Omit<TCertificateFromProfileResponse, "privateKey">> => {
|
||||||
const profile = await validateProfileAndPermissions(
|
const profile = await validateProfileAndPermissions(
|
||||||
profileId,
|
profileId,
|
||||||
@@ -582,7 +583,8 @@ export const certificateV3ServiceFactory = ({
|
|||||||
notAfter: normalizeDateForApi(notAfter),
|
notAfter: normalizeDateForApi(notAfter),
|
||||||
signatureAlgorithm: effectiveSignatureAlgorithm,
|
signatureAlgorithm: effectiveSignatureAlgorithm,
|
||||||
keyAlgorithm: effectiveKeyAlgorithm,
|
keyAlgorithm: effectiveKeyAlgorithm,
|
||||||
isFromProfile: true
|
isFromProfile: true,
|
||||||
|
allowEmptyCommonName
|
||||||
});
|
});
|
||||||
|
|
||||||
const cert = await certificateDAL.findOne({ serialNumber, caId: ca.id });
|
const cert = await certificateDAL.findOne({ serialNumber, caId: ca.id });
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export type TSignCertificateFromProfileDTO = {
|
|||||||
notAfter?: Date;
|
notAfter?: Date;
|
||||||
enrollmentType: EnrollmentType;
|
enrollmentType: EnrollmentType;
|
||||||
removeRootsFromChain?: boolean;
|
removeRootsFromChain?: boolean;
|
||||||
|
allowEmptyCommonName?: boolean;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TOrderCertificateFromProfileDTO = {
|
export type TOrderCertificateFromProfileDTO = {
|
||||||
|
|||||||
Reference in New Issue
Block a user