Update cypress docker version, generate custom certs each time

This commit is contained in:
Jamie Curnow
2026-05-19 08:04:49 +10:00
parent ee1f7ba551
commit f53bf88f4d
10 changed files with 133 additions and 289 deletions
+86 -231
View File
@@ -1,9 +1,9 @@
import fs from "node:fs"; import fs from "node:fs";
import https from "node:https"; import https from "node:https";
import path from "path";
import { ZipArchive } from "archiver"; import { ZipArchive } from "archiver";
import _ from "lodash"; import _ from "lodash";
import moment from "moment"; import moment from "moment";
import path from "path";
import { ProxyAgent } from "proxy-agent"; import { ProxyAgent } from "proxy-agent";
import tempWrite from "temp-write"; import tempWrite from "temp-write";
import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" }; import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" };
@@ -29,11 +29,7 @@ const omissions = () => {
}; };
const internalCertificate = { const internalCertificate = {
allowedSslFiles: [ allowedSslFiles: ["certificate", "certificate_key", "intermediate_certificate"],
"certificate",
"certificate_key",
"intermediate_certificate",
],
intervalTimeout: 1000 * 60 * 60, // 1 hour intervalTimeout: 1000 * 60 * 60, // 1 hour
interval: null, interval: null,
intervalProcessing: false, intervalProcessing: false,
@@ -60,10 +56,7 @@ const internalCertificate = {
); );
const expirationThreshold = moment() const expirationThreshold = moment()
.add( .add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1])
internalCertificate.renewBeforeExpirationBy[0],
internalCertificate.renewBeforeExpirationBy[1],
)
.format("YYYY-MM-DD HH:mm:ss"); .format("YYYY-MM-DD HH:mm:ss");
// Fetch all the letsencrypt certs from the db that will expire within the configured threshold // Fetch all the letsencrypt certs from the db that will expire within the configured threshold
@@ -144,18 +137,12 @@ const internalCertificate = {
// 6. Re-instate previously disabled hosts // 6. Re-instate previously disabled hosts
// 1. Find out any hosts that are using any of the hostnames in this cert // 1. Find out any hosts that are using any of the hostnames in this cert
const inUseResult = await internalHost.getHostsWithDomains( const inUseResult = await internalHost.getHostsWithDomains(certificate.domain_names);
certificate.domain_names,
);
// 2. Disable them in nginx temporarily // 2. Disable them in nginx temporarily
await internalCertificate.disableInUseHosts(inUseResult); await internalCertificate.disableInUseHosts(inUseResult);
const user = await userModel const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first();
.query()
.where("is_deleted", 0)
.andWhere("id", data.owner_user_id)
.first();
if (!user?.email) { if (!user?.email) {
throw new error.ValidationError( throw new error.ValidationError(
"A valid email address must be set on your user account to use Let's Encrypt", "A valid email address must be set on your user account to use Let's Encrypt",
@@ -167,10 +154,7 @@ const internalCertificate = {
try { try {
await internalNginx.reload(); await internalNginx.reload();
// 4. Request cert // 4. Request cert
await internalCertificate.requestLetsEncryptSslWithDnsChallenge( await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate, user.email);
certificate,
user.email,
);
await internalNginx.reload(); await internalNginx.reload();
// 6. Re-instate previously disabled hosts // 6. Re-instate previously disabled hosts
await internalCertificate.enableInUseHosts(inUseResult); await internalCertificate.enableInUseHosts(inUseResult);
@@ -187,10 +171,7 @@ const internalCertificate = {
await internalNginx.reload(); await internalNginx.reload();
setTimeout(() => {}, 5000); setTimeout(() => {}, 5000);
// 4. Request cert // 4. Request cert
await internalCertificate.requestLetsEncryptSsl( await internalCertificate.requestLetsEncryptSsl(certificate, user.email);
certificate,
user.email,
);
// 5. Remove LE config // 5. Remove LE config
await internalNginx.deleteLetsEncryptRequestConfig(certificate); await internalNginx.deleteLetsEncryptRequestConfig(certificate);
await internalNginx.reload(); await internalNginx.reload();
@@ -214,9 +195,7 @@ const internalCertificate = {
const savedRow = await certificateModel const savedRow = await certificateModel
.query() .query()
.patchAndFetchById(certificate.id, { .patchAndFetchById(certificate.id, {
expires_on: moment(certInfo.dates.to, "X").format( expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
"YYYY-MM-DD HH:mm:ss",
),
}) })
.then(utils.omitRow(omissions())); .then(utils.omitRow(omissions()));
@@ -225,11 +204,7 @@ const internalCertificate = {
letsencrypt_certificate: certInfo, letsencrypt_certificate: certInfo,
}); });
await internalCertificate.addCreatedAuditLog( await internalCertificate.addCreatedAuditLog(access, certificate.id, savedRow);
access,
certificate.id,
savedRow,
);
return savedRow; return savedRow;
} catch (err) { } catch (err) {
@@ -247,11 +222,7 @@ const internalCertificate = {
data.meta = _.assign({}, data.meta || {}, certificate.meta); data.meta = _.assign({}, data.meta || {}, certificate.meta);
// Add to audit log // Add to audit log
await internalCertificate.addCreatedAuditLog( await internalCertificate.addCreatedAuditLog(access, certificate.id, utils.omitRow(omissions())(data));
access,
certificate.id,
utils.omitRow(omissions())(data),
);
return utils.omitRow(omissions())(certificate); return utils.omitRow(omissions())(certificate);
}, },
@@ -350,9 +321,7 @@ const internalCertificate = {
row.proxy_hosts = utils.omitRows(["is_deleted"])(row.proxy_hosts); row.proxy_hosts = utils.omitRows(["is_deleted"])(row.proxy_hosts);
} }
if (typeof row.redirection_hosts !== "undefined") { if (typeof row.redirection_hosts !== "undefined") {
row.redirection_hosts = utils.omitRows(["is_deleted"])( row.redirection_hosts = utils.omitRows(["is_deleted"])(row.redirection_hosts);
row.redirection_hosts,
);
} }
if (typeof row.dead_hosts !== "undefined") { if (typeof row.dead_hosts !== "undefined") {
row.dead_hosts = utils.omitRows(["is_deleted"])(row.dead_hosts); row.dead_hosts = utils.omitRows(["is_deleted"])(row.dead_hosts);
@@ -375,9 +344,7 @@ const internalCertificate = {
if (certificate.provider === "letsencrypt") { if (certificate.provider === "letsencrypt") {
const zipDirectory = internalCertificate.getLiveCertPath(data.id); const zipDirectory = internalCertificate.getLiveCertPath(data.id);
if (!fs.existsSync(zipDirectory)) { if (!fs.existsSync(zipDirectory)) {
throw new error.ItemNotFoundError( throw new error.ItemNotFoundError(`Certificate ${certificate.nice_name} does not exists`);
`Certificate ${certificate.nice_name} does not exists`,
);
} }
const certFiles = fs const certFiles = fs
@@ -394,9 +361,7 @@ const internalCertificate = {
fileName: opName, fileName: opName,
}; };
} }
throw new error.ValidationError( throw new error.ValidationError("Only Let'sEncrypt certificates can be downloaded");
"Only Let'sEncrypt certificates can be downloaded",
);
}, },
/** /**
@@ -505,10 +470,7 @@ const internalCertificate = {
* @returns {Promise} * @returns {Promise}
*/ */
getCount: async (userId, visibility) => { getCount: async (userId, visibility) => {
const query = certificateModel const query = certificateModel.query().count("id as count").where("is_deleted", 0);
.query()
.count("id as count")
.where("is_deleted", 0);
if (visibility !== "all") { if (visibility !== "all") {
query.andWhere("owner_user_id", userId); query.andWhere("owner_user_id", userId);
@@ -556,17 +518,13 @@ const internalCertificate = {
}); });
}).then(() => { }).then(() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.writeFile( fs.writeFile(`${dir}/privkey.pem`, certificate.meta.certificate_key, (err) => {
`${dir}/privkey.pem`, if (err) {
certificate.meta.certificate_key, reject(err);
(err) => { } else {
if (err) { resolve();
reject(err); }
} else { });
resolve();
}
},
);
}); });
}); });
}, },
@@ -639,9 +597,7 @@ const internalCertificate = {
upload: async (access, data) => { upload: async (access, data) => {
const row = await internalCertificate.get(access, { id: data.id }); const row = await internalCertificate.get(access, { id: data.id });
if (row.provider !== "other") { if (row.provider !== "other") {
throw new error.ValidationError( throw new error.ValidationError("Cannot upload certificates for this type of provider");
"Cannot upload certificates for this type of provider",
);
} }
const validations = await internalCertificate.validate(data); const validations = await internalCertificate.validate(data);
@@ -657,12 +613,8 @@ const internalCertificate = {
const certificate = await internalCertificate.update(access, { const certificate = await internalCertificate.update(access, {
id: data.id, id: data.id,
expires_on: moment(validations.certificate.dates.to, "X").format( expires_on: moment(validations.certificate.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
"YYYY-MM-DD HH:mm:ss", domain_names: validations.certificate.cn ? [validations.certificate.cn] : [],
),
domain_names: validations.certificate.cn
? [validations.certificate.cn]
: [],
meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later
}); });
@@ -686,9 +638,7 @@ const internalCertificate = {
}, 10000); }, 10000);
try { try {
const result = await utils.exec( const result = await utils.exec(`openssl pkey -in ${filepath} -check -noout 2>&1 `);
`openssl pkey -in ${filepath} -check -noout 2>&1 `,
);
clearTimeout(failTimeout); clearTimeout(failTimeout);
if (!result.toLowerCase().includes("key is valid")) { if (!result.toLowerCase().includes("key is valid")) {
throw new error.ValidationError(`Result Validation Error: ${result}`); throw new error.ValidationError(`Result Validation Error: ${result}`);
@@ -698,10 +648,7 @@ const internalCertificate = {
} catch (err) { } catch (err) {
clearTimeout(failTimeout); clearTimeout(failTimeout);
fs.unlinkSync(filepath); fs.unlinkSync(filepath);
throw new error.ValidationError( throw new error.ValidationError(`Certificate Key is not valid (${err.message})`, err);
`Certificate Key is not valid (${err.message})`,
err,
);
} }
}, },
@@ -715,10 +662,7 @@ const internalCertificate = {
getCertificateInfo: async (certificate, throwExpired) => { getCertificateInfo: async (certificate, throwExpired) => {
const filepath = await tempWrite(certificate); const filepath = await tempWrite(certificate);
try { try {
const certData = await internalCertificate.getCertificateInfoFromFile( const certData = await internalCertificate.getCertificateInfoFromFile(filepath, throwExpired);
filepath,
throwExpired,
);
fs.unlinkSync(filepath); fs.unlinkSync(filepath);
return certData; return certData;
} catch (err) { } catch (err) {
@@ -738,14 +682,8 @@ const internalCertificate = {
const certData = {}; const certData = {};
try { try {
const result = await utils.execFile("openssl", [ const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"]);
"x509",
"-in",
certificateFile,
"-subject",
"-noout",
]);
console.log("openssl result: ", result);
// Examples: // Examples:
// subject=CN = *.jc21.com // subject=CN = *.jc21.com
// subject=CN = something.example.com // subject=CN = something.example.com
@@ -756,13 +694,7 @@ const internalCertificate = {
certData.cn = match[1].trim(); certData.cn = match[1].trim();
} }
const result2 = await utils.execFile("openssl", [ const result2 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-issuer", "-noout"]);
"x509",
"-in",
certificateFile,
"-issuer",
"-noout",
]);
// Examples: // Examples:
// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 // issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
// issuer=C = US, O = Let's Encrypt, CN = E5 // issuer=C = US, O = Let's Encrypt, CN = E5
@@ -773,13 +705,7 @@ const internalCertificate = {
certData.issuer = match2[1]; certData.issuer = match2[1];
} }
const result3 = await utils.execFile("openssl", [ const result3 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-dates", "-noout"]);
"x509",
"-in",
certificateFile,
"-dates",
"-noout",
]);
// notBefore=Jul 14 04:04:29 2018 GMT // notBefore=Jul 14 04:04:29 2018 GMT
// notAfter=Oct 12 04:04:29 2018 GMT // notAfter=Oct 12 04:04:29 2018 GMT
let validFrom = null; let validFrom = null;
@@ -791,10 +717,7 @@ const internalCertificate = {
const match = regex.exec(str.trim()); const match = regex.exec(str.trim());
if (match && typeof match[2] !== "undefined") { if (match && typeof match[2] !== "undefined") {
const date = Number.parseInt( const date = Number.parseInt(moment(match[2], "MMM DD HH:mm:ss YYYY z").format("X"), 10);
moment(match[2], "MMM DD HH:mm:ss YYYY z").format("X"),
10,
);
if (match[1].toLowerCase() === "notbefore") { if (match[1].toLowerCase() === "notbefore") {
validFrom = date; validFrom = date;
@@ -806,15 +729,10 @@ const internalCertificate = {
}); });
if (!validFrom || !validTo) { if (!validFrom || !validTo) {
throw new error.ValidationError( throw new error.ValidationError(`Could not determine dates from certificate: ${result}`);
`Could not determine dates from certificate: ${result}`,
);
} }
if ( if (throw_expired && validTo < Number.parseInt(moment().format("X"), 10)) {
throw_expired &&
validTo < Number.parseInt(moment().format("X"), 10)
) {
throw new error.ValidationError("Certificate has expired"); throw new error.ValidationError("Certificate has expired");
} }
@@ -825,10 +743,7 @@ const internalCertificate = {
return certData; return certData;
} catch (err) { } catch (err) {
throw new error.ValidationError( throw new error.ValidationError(`Certificate is not valid (${err.message})`, err);
`Certificate is not valid (${err.message})`,
err,
);
} }
}, },
@@ -915,11 +830,7 @@ const internalCertificate = {
const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`; const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`;
fs.mkdirSync("/etc/letsencrypt/credentials", { recursive: true }); fs.mkdirSync("/etc/letsencrypt/credentials", { recursive: true });
fs.writeFileSync( fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, { mode: 0o600 });
credentialsLocation,
certificate.meta.dns_provider_credentials,
{ mode: 0o600 },
);
// Whether the plugin has a --<name>-credentials argument // Whether the plugin has a --<name>-credentials argument
const hasConfigArg = certificate.meta.dns_provider !== "route53"; const hasConfigArg = certificate.meta.dns_provider !== "route53";
@@ -947,10 +858,7 @@ const internalCertificate = {
]; ];
if (hasConfigArg) { if (hasConfigArg) {
args.push( args.push(`--${dnsPlugin.full_plugin_name}-credentials`, credentialsLocation);
`--${dnsPlugin.full_plugin_name}-credentials`,
credentialsLocation,
);
} }
if (certificate.meta.propagation_seconds !== undefined) { if (certificate.meta.propagation_seconds !== undefined) {
args.push( args.push(
@@ -964,10 +872,7 @@ const internalCertificate = {
args.push("--key-type", certificate.meta.key_type); args.push("--key-type", certificate.meta.key_type);
} }
const adds = internalCertificate.getAdditionalCertbotArgs( const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
certificate.id,
certificate.meta.dns_provider,
);
args.push(...adds.args); args.push(...adds.args);
logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -1003,13 +908,9 @@ const internalCertificate = {
`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`, `${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`,
); );
const updatedCertificate = await certificateModel const updatedCertificate = await certificateModel.query().patchAndFetchById(certificate.id, {
.query() expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"),
.patchAndFetchById(certificate.id, { });
expires_on: moment(certInfo.dates.to, "X").format(
"YYYY-MM-DD HH:mm:ss",
),
});
// Add to audit log // Add to audit log
await internalAuditLog.add(access, { await internalAuditLog.add(access, {
@@ -1022,9 +923,7 @@ const internalCertificate = {
return updatedCertificate; return updatedCertificate;
} }
throw new error.ValidationError( throw new error.ValidationError("Only Let'sEncrypt certificates can be renewed");
"Only Let'sEncrypt certificates can be renewed",
);
}, },
/** /**
@@ -1058,10 +957,7 @@ const internalCertificate = {
args.push("--key-type", certificate.meta.key_type); args.push("--key-type", certificate.meta.key_type);
} }
const adds = internalCertificate.getAdditionalCertbotArgs( const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
certificate.id,
certificate.meta.dns_provider,
);
args.push(...adds.args); args.push(...adds.args);
logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -1107,10 +1003,7 @@ const internalCertificate = {
args.push("--key-type", certificate.meta.key_type); args.push("--key-type", certificate.meta.key_type);
} }
const adds = internalCertificate.getAdditionalCertbotArgs( const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider);
certificate.id,
certificate.meta.dns_provider,
);
args.push(...adds.args); args.push(...adds.args);
logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`);
@@ -1150,9 +1043,7 @@ const internalCertificate = {
try { try {
const result = await utils.execFile(certbotCommand, args, adds.opts); const result = await utils.execFile(certbotCommand, args, adds.opts);
await utils.exec( await utils.exec(`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`);
`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`,
);
logger.info(result); logger.info(result);
return result; return result;
} catch (err) { } catch (err) {
@@ -1169,10 +1060,7 @@ const internalCertificate = {
*/ */
hasLetsEncryptSslCerts: (certificate) => { hasLetsEncryptSslCerts: (certificate) => {
const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id); const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id);
return ( return fs.existsSync(`${letsencryptPath}/fullchain.pem`) && fs.existsSync(`${letsencryptPath}/privkey.pem`);
fs.existsSync(`${letsencryptPath}/fullchain.pem`) &&
fs.existsSync(`${letsencryptPath}/privkey.pem`)
);
}, },
/** /**
@@ -1186,24 +1074,15 @@ const internalCertificate = {
disableInUseHosts: async (inUseResult) => { disableInUseHosts: async (inUseResult) => {
if (inUseResult?.total_count) { if (inUseResult?.total_count) {
if (inUseResult?.proxy_hosts.length) { if (inUseResult?.proxy_hosts.length) {
await internalNginx.bulkDeleteConfigs( await internalNginx.bulkDeleteConfigs("proxy_host", inUseResult.proxy_hosts);
"proxy_host",
inUseResult.proxy_hosts,
);
} }
if (inUseResult?.redirection_hosts.length) { if (inUseResult?.redirection_hosts.length) {
await internalNginx.bulkDeleteConfigs( await internalNginx.bulkDeleteConfigs("redirection_host", inUseResult.redirection_hosts);
"redirection_host",
inUseResult.redirection_hosts,
);
} }
if (inUseResult?.dead_hosts.length) { if (inUseResult?.dead_hosts.length) {
await internalNginx.bulkDeleteConfigs( await internalNginx.bulkDeleteConfigs("dead_host", inUseResult.dead_hosts);
"dead_host",
inUseResult.dead_hosts,
);
} }
} }
}, },
@@ -1219,24 +1098,15 @@ const internalCertificate = {
enableInUseHosts: async (inUseResult) => { enableInUseHosts: async (inUseResult) => {
if (inUseResult.total_count) { if (inUseResult.total_count) {
if (inUseResult.proxy_hosts.length) { if (inUseResult.proxy_hosts.length) {
await internalNginx.bulkGenerateConfigs( await internalNginx.bulkGenerateConfigs("proxy_host", inUseResult.proxy_hosts);
"proxy_host",
inUseResult.proxy_hosts,
);
} }
if (inUseResult.redirection_hosts.length) { if (inUseResult.redirection_hosts.length) {
await internalNginx.bulkGenerateConfigs( await internalNginx.bulkGenerateConfigs("redirection_host", inUseResult.redirection_hosts);
"redirection_host",
inUseResult.redirection_hosts,
);
} }
if (inUseResult.dead_hosts.length) { if (inUseResult.dead_hosts.length) {
await internalNginx.bulkGenerateConfigs( await internalNginx.bulkGenerateConfigs("dead_host", inUseResult.dead_hosts);
"dead_host",
inUseResult.dead_hosts,
);
} }
} }
}, },
@@ -1251,8 +1121,7 @@ const internalCertificate = {
await access.can("certificates:list"); await access.can("certificates:list");
// Create a test challenge file // Create a test challenge file
const testChallengeDir = const testChallengeDir = "/data/letsencrypt-acme-challenge/.well-known/acme-challenge";
"/data/letsencrypt-acme-challenge/.well-known/acme-challenge";
const testChallengeFile = `${testChallengeDir}/test-challenge`; const testChallengeFile = `${testChallengeDir}/test-challenge`;
fs.mkdirSync(testChallengeDir, { recursive: true }); fs.mkdirSync(testChallengeDir, { recursive: true });
fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" }); fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" });
@@ -1284,42 +1153,38 @@ const internalCertificate = {
}; };
const result = await new Promise((resolve) => { const result = await new Promise((resolve) => {
const req = https.request( const req = https.request("https://www.site24x7.com/tools/restapi-tester", options, (res) => {
"https://www.site24x7.com/tools/restapi-tester", let responseBody = "";
options,
(res) => {
let responseBody = "";
res.on("data", (chunk) => { res.on("data", (chunk) => {
responseBody = responseBody + chunk; responseBody = responseBody + chunk;
}); });
res.on("end", () => { res.on("end", () => {
try { try {
const parsedBody = JSON.parse(`${responseBody}`); const parsedBody = JSON.parse(`${responseBody}`);
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
logger.warn( logger.warn(
`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`, `Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`,
); );
resolve(undefined);
} else {
resolve(parsedBody);
}
} catch (err) {
if (res.statusCode !== 200) {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned`,
);
} else {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because response failed to be parsed: ${err.message}`,
);
}
resolve(undefined); resolve(undefined);
} else {
resolve(parsedBody);
} }
}); } catch (err) {
}, if (res.statusCode !== 200) {
); logger.warn(
`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned`,
);
} else {
logger.warn(
`Failed to test HTTP challenge for domain ${domain} because response failed to be parsed: ${err.message}`,
);
}
resolve(undefined);
}
});
});
// Make sure to write the request body. // Make sure to write the request body.
req.write(formBody); req.write(formBody);
@@ -1340,10 +1205,7 @@ const internalCertificate = {
); );
return `other:${result.error.msg}`; return `other:${result.error.msg}`;
} }
if ( if (`${result.responsecode}` === "200" && result.htmlresponse === "Success") {
`${result.responsecode}` === "200" &&
result.htmlresponse === "Success"
) {
// Server exists and has responded with the correct data // Server exists and has responded with the correct data
return "ok"; return "ok";
} }
@@ -1357,26 +1219,19 @@ const internalCertificate = {
} }
if (`${result.responsecode}` === "404") { if (`${result.responsecode}` === "404") {
// Server exists but responded with a 404 // Server exists but responded with a 404
logger.info( logger.info(`HTTP challenge test failed for domain ${domain} because code 404 was returned`);
`HTTP challenge test failed for domain ${domain} because code 404 was returned`,
);
return "404"; return "404";
} }
if ( if (
`${result.responsecode}` === "0" || `${result.responsecode}` === "0" ||
(typeof result.reason === "string" && (typeof result.reason === "string" && result.reason.toLowerCase() === "host unavailable")
result.reason.toLowerCase() === "host unavailable")
) { ) {
// Server does not exist at domain // Server does not exist at domain
logger.info( logger.info(`HTTP challenge test failed for domain ${domain} the host was not found`);
`HTTP challenge test failed for domain ${domain} the host was not found`,
);
return "no-host"; return "no-host";
} }
// Other errors // Other errors
logger.info( logger.info(`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`);
`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`,
);
return `other:${result.responsecode}`; return `other:${result.responsecode}`;
}, },
+1 -1
View File
@@ -1,4 +1,4 @@
FROM cypress/included:15.11.0 FROM cypress/included:15.15.0
# Disable Cypress CLI colors # Disable Cypress CLI colors
ENV FORCE_COLOR=0 ENV FORCE_COLOR=0
+1
View File
@@ -5,6 +5,7 @@ describe('Certificates endpoints', () => {
let certID; let certID;
before(() => { before(() => {
cy.createCustomCerts();
cy.resetUsers(); cy.resetUsers();
cy.getToken().then((tok) => { cy.getToken().then((tok) => {
token = tok; token = tok;
+1 -1
View File
@@ -2,7 +2,7 @@
describe('LDAP with Authentik', () => { describe('LDAP with Authentik', () => {
let _token; let _token;
if (Cypress.env('skipStackCheck') === 'true' || Cypress.env('stack') === 'postgres') { if (cy.env('skipStackCheck') === 'true' || cy.env('stack') === 'postgres') {
before(() => { before(() => {
cy.resetUsers(); cy.resetUsers();
+1 -1
View File
@@ -2,7 +2,7 @@
describe('OAuth with Authentik', () => { describe('OAuth with Authentik', () => {
let _token; let _token;
if (Cypress.env('skipStackCheck') === 'true' || Cypress.env('stack') === 'postgres') { if (cy.env('skipStackCheck') === 'true' || cy.env('stack') === 'postgres') {
before(() => { before(() => {
cy.getToken().then((tok) => { cy.getToken().then((tok) => {
@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1n9j9C5Bes1nd
qACDckERauxXVNKCnUlUM1buGBx1xc+j2e2Ar23wUJJuWBY18VfT8yqfqVDktO2w
rbmvZvLuPmXePOKbIKS+XXh+2NG9L5bDG9rwGFCRXnbQj+GWCdMfzx14+CR1IHge
Yz6Cv/Si2/LJPCh/CoBfM4hUQJON3lxAWrWBpdbZnKYMrxuPBRfW9OuzTbCVXToQ
oxRAHiOR9081Xn1WeoKr7kVBIa5UphlvWXa12w1YmUwJu7YndnJGIavLWeNCVc7Z
Eo+nS8Wr/4QWicatIWZXpVaEOPhRoeplQDxNWg5b/Q26rYoVd7PrCmRs7sVcH79X
zGONeH1PAgMBAAECggEAANb3Wtwl07pCjRrMvc7WbC0xYIn82yu8/g2qtjkYUJcU
ia5lQbYN7RGCS85Oc/tkq48xQEG5JQWNH8b918jDEMTrFab0aUEyYcru1q9L8PL6
YHaNgZSrMrDcHcS8h0QOXNRJT5jeGkiHJaTR0irvB526tqF3knbK9yW22KTfycUe
a0Z9voKn5xRk1DCbHi/nk2EpT7xnjeQeLFaTIRXbS68omkr4YGhwWm5OizoyEGZu
W0Zum5BkQyMr6kor3wdxOTG97ske2rcyvvHi+ErnwL0xBv0qY0Dhe8DpuXpDezqw
o72yY8h31Fu84i7sAj24YuE5Df8DozItFXQpkgbQ6QKBgQDPrufhvIFm2S/MzBdW
H8JxY7CJlJPyxOvc1NIl9RczQGAQR90kx52cgIcuIGEG6/wJ/xnGfMmW40F0DnQ+
N+oLgB9SFxeLkRb7s9Z/8N3uIN8JJFYcerEOiRQeN2BXEEWJ7bUThNtsVrAcKoUh
ELsDmnHW/3V+GKwhd0vpk842+wKBgQDf4PGLG9PTE5tlAoyHFodJRd2RhTJQkwsU
MDNjLJ+KecLv+Nl+QiJhoflG1ccqtSFlBSCG067CDQ5LV0xm3mLJ7pfJoMgjcq31
qjEmX4Ls91GuVOPtbwst3yFKjsHaSoKB5fBvWRcKFpBUezM7Qcw2JP3+dQT+bQIq
cMTkRWDSvQKBgQDOdCQFDjxg/lR7NQOZ1PaZe61aBz5P3pxNqa7ClvMaOsuEQ7w9
vMYcdtRq8TsjA2JImbSI0TIg8gb2FQxPcYwTJKl+FICOeIwtaSg5hTtJZpnxX5LO
utTaC0DZjNkTk5RdOdWA8tihyUdGqKoxJY2TVmwGe2rUEDjFB++J4inkEwKBgB6V
g0nmtkxanFrzOzFlMXwgEEHF+Xaqb9QFNa/xs6XeNnREAapO7JV75Cr6H2hFMFe1
mJjyqCgYUoCWX3iaHtLJRnEkBtNY4kzyQB6m46LtsnnnXO/dwKA2oDyoPfFNRoDq
YatEd3JIXNU9s2T/+x7WdOBjKhh72dTkbPFmTPDdAoGAU6rlPBevqOFdObYxdPq8
EQWu44xqky3Mf5sBpOwtu6rqCYuziLiN7K4sjN5GD5mb1cEU+oS92ZiNcUQ7MFXk
8yTYZ7U0VcXyAcpYreWwE8thmb0BohJBr+Mp3wLTx32x0HKdO6vpUa0d35LUTUmM
RrKmPK/msHKK/sVHiL+NFqo=
-----END PRIVATE KEY-----
@@ -1,26 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEYDCCAsigAwIBAgIRAPoSC0hvitb26ODMlsH6YbowDQYJKoZIhvcNAQELBQAw
gZExHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEzMDEGA1UECwwqamN1
cm5vd0BKYW1pZXMtTGFwdG9wLmxvY2FsIChKYW1pZSBDdXJub3cpMTowOAYDVQQD
DDFta2NlcnQgamN1cm5vd0BKYW1pZXMtTGFwdG9wLmxvY2FsIChKYW1pZSBDdXJu
b3cpMB4XDTI0MTAwOTA3MjIxN1oXDTI3MDEwOTA3MjIxN1owXjEnMCUGA1UEChMe
bWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMTMwMQYDVQQLDCpqY3Vybm93
QEphbWllcy1MYXB0b3AubG9jYWwgKEphbWllIEN1cm5vdykwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC1n9j9C5Bes1ndqACDckERauxXVNKCnUlUM1bu
GBx1xc+j2e2Ar23wUJJuWBY18VfT8yqfqVDktO2wrbmvZvLuPmXePOKbIKS+XXh+
2NG9L5bDG9rwGFCRXnbQj+GWCdMfzx14+CR1IHgeYz6Cv/Si2/LJPCh/CoBfM4hU
QJON3lxAWrWBpdbZnKYMrxuPBRfW9OuzTbCVXToQoxRAHiOR9081Xn1WeoKr7kVB
Ia5UphlvWXa12w1YmUwJu7YndnJGIavLWeNCVc7ZEo+nS8Wr/4QWicatIWZXpVaE
OPhRoeplQDxNWg5b/Q26rYoVd7PrCmRs7sVcH79XzGONeH1PAgMBAAGjZTBjMA4G
A1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSB
/vfmBUd4W7CvyEMl7YpMVQs8vTAbBgNVHREEFDASghB0ZXN0LmV4YW1wbGUuY29t
MA0GCSqGSIb3DQEBCwUAA4IBgQASwON/jPAHzcARSenY0ZGY1m5OVTYoQ/JWH0oy
l8SyFCQFEXt7UHDD/eTtLT0vMyc190nP57P8lTnZGf7hSinZz1B1d6V4cmzxpk0s
VXZT+irL6bJVJoMBHRpllKAhGULIo33baTrWFKA0oBuWx4AevSWKcLW5j87kEawn
ATCuMQ1I3ifR1mSlB7X8fb+vF+571q0NGuB3a42j6rdtXJ6SmH4+9B4qO0sfHDNt
IImpLCH/tycDpcYrGSCn1QrekFG1bSEh+Bb9i8rqMDSDsYrTFPZTuOQ3EtjGni9u
m+rEP3OyJg+md8c+0LVP7/UU4QWWnw3/Wolo5kSCxE8vNTFqi4GhVbdLnUtcIdTV
XxuR6cKyW87Snj1a0nG76ZLclt/akxDhtzqeV60BO0p8pmiev8frp+E94wFNYCmp
1cr3CnMEGRaficLSDFC6EBENzlZW2BQT6OMIV+g0NBgSyQe39s2zcdEl5+SzDVuw
hp8bJUp/QN7pnOVCDbjTQ+HVMXw=
-----END CERTIFICATE-----
+24 -1
View File
@@ -10,6 +10,7 @@
// //
import 'cypress-wait-until'; import 'cypress-wait-until';
import { createCA, createCert } from 'mkcert';
Cypress.Commands.add('randomString', (length) => { Cypress.Commands.add('randomString', (length) => {
let result = ''; let result = '';
@@ -49,7 +50,7 @@ Cypress.Commands.add("validateSwaggerFile", (url, savePath) => {
*/ */
Cypress.Commands.add('validateSwaggerSchema', (method, code, path, data) => { Cypress.Commands.add('validateSwaggerSchema', (method, code, path, data) => {
cy.task('validateSwaggerSchema', { cy.task('validateSwaggerSchema', {
file: Cypress.env('swaggerBase'), file: cy.env('swaggerBase'),
endpoint: path, endpoint: path,
method: method, method: method,
statusCode: code, statusCode: code,
@@ -151,3 +152,25 @@ Cypress.Commands.add('waitForCertificateStatus', (token, certID, expected, timeo
interval: 5000 interval: 5000
}); });
}); });
// Creates CA files for testing, if they already exist they will be deleted
// and recreated with the same content. This is to ensure that the files exist
// for testing and are in a known state.
Cypress.Commands.add('createCustomCerts', async () => {
const ca = await createCA({
organization: "NPM CA",
countryCode: "AU",
state: "QLD",
locality: "Brisbane",
validity: 365
});
const cert = await createCert({
ca: { key: ca.key, cert: ca.cert },
domains: ["test.example.com"],
validity: 365
});
cy.writeFile(`${config.fixturesFolder}/test.example.com.pem`, cert.cert);
cy.writeFile(`${config.fixturesFolder}/test.example.com-key.pem`, cert.key);
});
+1
View File
@@ -17,6 +17,7 @@
"eslint-plugin-cypress": "^6.4.1", "eslint-plugin-cypress": "^6.4.1",
"form-data": "^4.0.5", "form-data": "^4.0.5",
"lodash": "^4.18.1", "lodash": "^4.18.1",
"mkcert": "^3.2.0",
"mocha": "^11.7.5", "mocha": "^11.7.5",
"mocha-junit-reporter": "^2.2.1" "mocha-junit-reporter": "^2.2.1"
}, },
+18
View File
@@ -567,6 +567,11 @@ combined-stream@^1.0.8, combined-stream@~1.0.6:
dependencies: dependencies:
delayed-stream "~1.0.0" delayed-stream "~1.0.0"
commander@^11.0.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906"
integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==
commander@^6.2.1: commander@^6.2.1:
version "6.2.1" version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
@@ -1561,6 +1566,14 @@ minizlib@^3.1.0:
dependencies: dependencies:
minipass "^7.1.2" minipass "^7.1.2"
mkcert@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/mkcert/-/mkcert-3.2.0.tgz#6c4d58bbb31dbf76efd24f197d454702b14020f4"
integrity sha512-026Eivq9RoOjOuLJGzbhGwXUAjBxRX11Z7Jbm4/7lqT/Av+XNy9SPrJte6+UpEt7i+W3e/HZYxQqlQcqXZWSzg==
dependencies:
commander "^11.0.0"
node-forge "^1.3.1"
mkdirp@^3.0.0: mkdirp@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50"
@@ -1628,6 +1641,11 @@ node-fetch@^3.3.2:
fetch-blob "^3.1.4" fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10" formdata-polyfill "^4.0.10"
node-forge@^1.3.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.4.0.tgz#1c7b7d8bdc2d078739f58287d589d903a11b2fc2"
integrity sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==
npm-run-path@^4.0.0: npm-run-path@^4.0.0:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"