Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
Zoey 2024-10-19 18:18:26 +02:00
commit 1dbf57c2ba
6 changed files with 163 additions and 94 deletions

View file

@ -2,24 +2,23 @@ const _ = require('lodash');
const fs = require('fs'); const fs = require('fs');
const https = require('https'); const https = require('https');
const moment = require('moment'); const moment = require('moment');
const archiver = require('archiver');
const path = require('path');
const crypto = require('crypto');
const { isArray } = require('lodash');
const logger = require('../logger').ssl; const logger = require('../logger').ssl;
const error = require('../lib/error'); const error = require('../lib/error');
const utils = require('../lib/utils'); const utils = require('../lib/utils');
const certbot = require('../lib/certbot');
const certificateModel = require('../models/certificate'); const certificateModel = require('../models/certificate');
const dnsPlugins = require('../certbot-dns-plugins.json'); const dnsPlugins = require('../certbot-dns-plugins.json');
const internalAuditLog = require('./audit-log'); const internalAuditLog = require('./audit-log');
const internalNginx = require('./nginx'); const internalNginx = require('./nginx');
const certbot = require('../lib/certbot');
const archiver = require('archiver');
const crypto = require('crypto');
const path = require('path');
const { isArray } = require('lodash');
const certbotConfig = '/data/tls/certbot/config.ini'; const certbotCommand = 'certbot --logs-dir /tmp/certbot-log --work-dir /tmp/certbot-work --config-dir /data/tls/certbot --agree-tos -non-interactive --config /etc/tls/certbot.ini';
const certbotCommand = 'certbot --logs-dir /tmp/certbot-log --work-dir /tmp/certbot-work --config-dir /data/tls/certbot';
function omissions() { function omissions() {
return ['is_deleted']; return ['is_deleted', 'owner.is_deleted'];
} }
const internalCertificate = { const internalCertificate = {
@ -41,7 +40,7 @@ const internalCertificate = {
internalCertificate.intervalProcessing = true; internalCertificate.intervalProcessing = true;
logger.info('Renewing TLS certs close to expiry...'); logger.info('Renewing TLS certs close to expiry...');
const cmd = certbotCommand + ' renew --quiet ' + '--config "' + certbotConfig + '" ' + '--preferred-challenges "dns,http" ' + '--no-random-sleep-on-renew'; const cmd = '${certbotCommand} renew --quiet';
return utils return utils
.exec(cmd) .exec(cmd)
@ -121,7 +120,7 @@ const internalCertificate = {
// Request a new Cert using Certbot. Let the fun begin. // Request a new Cert using Certbot. Let the fun begin.
if (certificate.meta.dns_challenge) { if (certificate.meta.dns_challenge) {
return internalCertificate return internalCertificate
.requestLetsEncryptSslWithDnsChallenge(certificate) .requestCertbotWithDnsChallenge(certificate)
.then(() => { .then(() => {
return certificate; return certificate;
}) })
@ -131,7 +130,7 @@ const internalCertificate = {
}); });
} else { } else {
return internalCertificate return internalCertificate
.requestLetsEncryptSsl(certificate) .requestCertbot(certificate)
.then(() => { .then(() => {
return certificate; return certificate;
}) })
@ -156,6 +155,7 @@ const internalCertificate = {
.patchAndFetchById(certificate.id, { .patchAndFetchById(certificate.id, {
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'), expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss'),
}) })
.then(utils.omitRow(omissions()))
.then((saved_row) => { .then((saved_row) => {
// Add cert data for audit log // Add cert data for audit log
saved_row.meta = _.assign({}, saved_row.meta, { saved_row.meta = _.assign({}, saved_row.meta, {
@ -385,7 +385,7 @@ const internalCertificate = {
.then(() => { .then(() => {
if (row.provider === 'letsencrypt') { if (row.provider === 'letsencrypt') {
// Revoke the cert // Revoke the cert
return internalCertificate.revokeLetsEncryptSsl(row); return internalCertificate.revokeCertbot(row);
} }
}); });
}) })
@ -672,28 +672,22 @@ const internalCertificate = {
return utils return utils
.exec('openssl x509 -in ' + certificate_file + ' -subject -noout') .exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
.then((result) => { .then((result) => {
// subject=CN = something.example.com
const regex = /(?:subject=)?[^=]+=\s*(\S+)/gim; const regex = /(?:subject=)?[^=]+=\s*(\S+)/gim;
const match = regex.exec(result); const match = regex.exec(result);
if (match && typeof match[1] !== 'undefined') {
if (typeof match[1] === 'undefined') { certData['cn'] = match[1];
throw new error.ValidationError('Could not determine subject from certificate: ' + result);
} }
certData.cn = match[1];
}) })
.then(() => { .then(() => {
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout'); return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
}) })
.then((result) => { .then((result) => {
const regex = /^(?:issuer=)?(.*)$/gim; const regex = /^(?:issuer=)?(.*)$/gim;
const match = regex.exec(result); const match = regex.exec(result);
if (match && typeof match[1] !== 'undefined') {
if (typeof match[1] === 'undefined') { certData['issuer'] = match[1];
throw new error.ValidationError('Could not determine issuer from certificate: ' + result);
} }
certData.issuer = match[1];
}) })
.then(() => { .then(() => {
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout'); return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');
@ -766,16 +760,10 @@ const internalCertificate = {
* @param {Object} certificate the certificate row * @param {Object} certificate the certificate row
* @returns {Promise} * @returns {Promise}
*/ */
requestLetsEncryptSsl: (certificate) => { requestCertbot: (certificate) => {
logger.info('Requesting Certbot certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info('Requesting Certbot certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
let cmd = certbotCommand + ' certonly ' + '--config "' + certbotConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--authenticator webroot ' + '--preferred-challenges "http,dns" ' + '--domains "' + certificate.domain_names.join(',') + '"'; let cmd = `${certbotCommand} certonly --cert-name "npm-${certificate.id}" --domains "${certificate.domain_names.join(',')}" --server "${process.env.ACME_SERVER}" --authenticator webroot --webroot-path "/tmp/acme-challenge"`;
if (certificate.meta.letsencrypt_email === '') {
cmd = cmd + ' --register-unsafely-without-email ';
} else {
cmd = cmd + ' --email "' + certificate.meta.letsencrypt_email + '" ';
}
logger.info('Command:', cmd); logger.info('Command:', cmd);
@ -792,22 +780,20 @@ const internalCertificate = {
* @param {String} propagation_seconds the time to wait until the dns record should be changed * @param {String} propagation_seconds the time to wait until the dns record should be changed
* @returns {Promise} * @returns {Promise}
*/ */
requestLetsEncryptSslWithDnsChallenge: async (certificate) => { requestCertbotWithDnsChallenge: async (certificate) => {
await certbot.installPlugin(certificate.meta.dns_provider);
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider]; const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
if (!dnsPlugin) {
throw Error(`Unknown DNS provider: ${certificate.meta.dns_provider}`);
}
await certbot.installPlugin(certificate.meta.dns_provider);
logger.info(`Requesting Certbot certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); logger.info(`Requesting Certbot certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
const credentialsLocation = '/data/tls/certbot/credentials/credentials-' + certificate.id; const credentialsLocation = '/data/tls/certbot/credentials/credentials-' + certificate.id;
fs.mkdirSync('/data/tls/certbot/credentials', { recursive: true }); fs.mkdirSync('/data/tls/certbot/credentials', { recursive: true });
fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, { mode: 0o600 }); fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, { mode: 0o600 });
let mainCmd = certbotCommand + ' certonly ' + '--config "' + certbotConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--domains "' + certificate.domain_names.join(',') + '" ' + '--authenticator ' + dnsPlugin.full_plugin_name + ' ' + '--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"' + (certificate.meta.propagation_seconds !== undefined ? ' --' + dnsPlugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds : ''); let mainCmd = `${certbotCommand} certonly --cert-name "npm-${certificate.id}" --domains "${certificate.domain_names.join(',')}" --server "${process.env.ACME_SERVER}" --authenticator ${dnsPlugin.full_plugin_name} --${dnsPlugin.full_plugin_name}-credentials "${credentialsLocation}"`;
if (certificate.meta.letsencrypt_email === '') {
mainCmd = mainCmd + ' --register-unsafely-without-email ';
} else {
mainCmd = mainCmd + ' --email "' + certificate.meta.letsencrypt_email + '" ';
}
logger.info('Command:', mainCmd); logger.info('Command:', mainCmd);
@ -836,8 +822,7 @@ const internalCertificate = {
}) })
.then((certificate) => { .then((certificate) => {
if (certificate.provider === 'letsencrypt') { if (certificate.provider === 'letsencrypt') {
const renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl; const renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewCertbotWithDnsChallenge : internalCertificate.renewCertbot;
return renewMethod(certificate) return renewMethod(certificate)
.then(() => { .then(() => {
return internalCertificate.getCertificateInfoFromFile('/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem'); return internalCertificate.getCertificateInfoFromFile('/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem');
@ -867,20 +852,20 @@ const internalCertificate = {
}, },
/** /**
* @param {Object} certificate the certificate row * @param {Object} certificate the certificate row
* @returns {Promise} * @returns {Promise}
*/ */
renewLetsEncryptSsl: async (certificate) => { renewCertbot: async (certificate) => {
logger.info('Renewing Certbot certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info(`Renewing Certbot certificates for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
const cmdr = certbotCommand + ' revoke ' + '--config "' + certbotConfig + '" ' + '--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/privkey.pem" ' + '--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem" --no-delete-after-revoke'; const cmdr = `${certbotCommand} revoke --cert-path "/data/tls/certbot/live/npm-${certificate.id}/fullchain.pem" --cert-path "/data/tls/certbot/live/npm-${certificate.id}/privkey.pem" --no-delete-after-revoke`;
logger.info('Command:', cmdr); logger.info('Command:', cmdr);
const revokeResult = await utils.exec(cmdr); const revokeResult = await utils.exec(cmdr);
logger.info(revokeResult); logger.info(revokeResult);
const cmd = certbotCommand + ' renew --force-renewal ' + '--config "' + certbotConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--preferred-challenges "http,dns" ' + '--no-random-sleep-on-renew'; const cmd = `${certbotCommand} renew --force-renewal --cert-name "npm-${certificate.id}"`;
logger.info('Command:', cmd); logger.info('Command:', cmd);
@ -894,27 +879,27 @@ const internalCertificate = {
* @param {Object} certificate the certificate row * @param {Object} certificate the certificate row
* @returns {Promise} * @returns {Promise}
*/ */
renewLetsEncryptSslWithDnsChallenge: async (certificate) => { renewCertbotWithDnsChallenge: async (certificate) => {
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider]; const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
if (!dnsPlugin) { if (!dnsPlugin) {
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`); throw Error(`Unknown DNS provider: ${certificate.meta.dns_provider}`);
} }
await certbot.installPlugin(certificate.meta.dns_provider);
logger.info(`Renewing Certbot certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); logger.info(`Renewing Certbot certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
const mainCmdr = certbotCommand + ' revoke ' + '--config "' + certbotConfig + '" ' + '--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/privkey.pem" ' + '--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem" --no-delete-after-revoke'; const cmdr = `${certbotCommand} revoke --cert-path "/data/tls/certbot/live/npm-${certificate.id}/fullchain.pem" --cert-path "/data/tls/certbot/live/npm-${certificate.id}/privkey.pem" --no-delete-after-revoke`;
logger.info('Command:', mainCmdr); logger.info('Command:', cmdr);
const revokeResult = await utils.exec(mainCmdr); const revokeResult = await utils.exec(cmdr);
logger.info(revokeResult); logger.info(revokeResult);
const mainCmd = certbotCommand + ' renew --force-renewal ' + '--config "' + certbotConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--preferred-challenges "dns,http" ' + '--no-random-sleep-on-renew'; const cmd = `${certbotCommand} renew --force-renewal --cert-name "npm-${certificate.id}"`;
logger.info('Command:', mainCmd); logger.info('Command:', cmd);
const renewResult = await utils.exec(mainCmd); const renewResult = await utils.exec(cmd);
logger.info(renewResult); logger.info(renewResult);
return renewResult; return renewResult;
@ -925,10 +910,10 @@ const internalCertificate = {
* @param {Boolean} [throw_errors] * @param {Boolean} [throw_errors]
* @returns {Promise} * @returns {Promise}
*/ */
revokeLetsEncryptSsl: (certificate, throw_errors) => { revokeCertbot: (certificate, throw_errors) => {
logger.info('Revoking Certbot certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info('Revoking Certbot certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const mainCmd = certbotCommand + ' revoke ' + '--config "' + certbotConfig + '" ' + '--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/privkey.pem" ' + '--cert-path "/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem" ' + '--delete-after-revoke'; const mainCmd = `${certbotCommand} revoke --cert-path "/data/tls/certbot/live/npm-${certificate.id}/fullchain.pem" --cert-path "/data/tls/certbot/live/npm-${certificate.id}/privkey.pem" --no-delete-after-revoke`;
return utils return utils
.exec(mainCmd) .exec(mainCmd)
@ -955,16 +940,6 @@ const internalCertificate = {
}); });
}, },
/**
* @param {Object} certificate
* @returns {Boolean}
*/
hasLetsEncryptSslCerts: (certificate) => {
const letsencryptPath = '/data/tls/certbot/live/npm-' + certificate.id;
return fs.existsSync(letsencryptPath + '/fullchain.pem') && fs.existsSync(letsencryptPath + '/privkey.pem');
},
/** /**
* @param {Object} in_use_result * @param {Object} in_use_result
* @param {Number} in_use_result.total_count * @param {Number} in_use_result.total_count

View file

@ -148,15 +148,6 @@ module.exports = {
return instance.database.knex && instance.database.knex.client === 'better-sqlite3'; return instance.database.knex && instance.database.knex.client === 'better-sqlite3';
}, },
/**
* Are we running in debug mode?
*
* @returns {boolean}
*/
debug: function () {
return !!process.env.DEBUG;
},
/** /**
* Returns a public key * Returns a public key
* *
@ -176,11 +167,4 @@ module.exports = {
instance === null && configure(); instance === null && configure();
return instance.keys.key; return instance.keys.key;
}, },
/**
* @returns {boolean}
*/
useLetsencryptStaging: function () {
return !!process.env.LE_STAGING;
},
}; };

View file

@ -24,22 +24,34 @@
"description": "Nice Name for the custom certificate" "description": "Nice Name for the custom certificate"
}, },
"domain_names": { "domain_names": {
"$ref": "../common.json#/properties/domain_names" "description": "Domain Names separated by a comma",
"type": "array",
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "string",
"pattern": "^[^&| @!#%^();:/\\\\}{=+?<>,~`'\"]+$"
}
}, },
"expires_on": { "expires_on": {
"description": "Date and time of expiration", "description": "Date and time of expiration",
"readOnly": true, "readOnly": true,
"type": "string" "type": "string"
}, },
"owner": {
"$ref": "./user-object.json"
},
"meta": { "meta": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"letsencrypt_email": { "certificate": {
"type": "string" "type": "string",
"minLength": 1
}, },
"letsencrypt_agree": { "certificate_key": {
"type": "boolean" "type": "string",
"minLength": 1
}, },
"dns_challenge": { "dns_challenge": {
"type": "boolean" "type": "boolean"
@ -50,13 +62,18 @@
"dns_provider_credentials": { "dns_provider_credentials": {
"type": "string" "type": "string"
}, },
"letsencrypt_agree": {
"type": "boolean"
},
"letsencrypt_certificate": {
"type": "object"
},
"letsencrypt_email": {
"type": "string"
},
"propagation_seconds": { "propagation_seconds": {
"anyOf": [ "type": "integer",
{ "minimum": 0
"type": "integer",
"minimum": 0
}
]
} }
} }
} }

View file

@ -55,6 +55,25 @@
"certificate_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1n9j9C5Bes1nd\nqACDckERauxXVNKCnUlUM1buGBx1xc+j2e2Ar23wUJJuWBY18VfT8yqfqVDktO2w\nrbmvZvLuPmXePOKbIKS+XXh+2NG9L5bDG9rwGFCRXnbQj+GWCdMfzx14+CR1IHge\nYz6Cv/Si2/LJPCh/CoBfM4hUQJON3lxAWrWBpdbZnKYMrxuPBRfW9OuzTbCVXToQ\noxRAHiOR9081Xn1WeoKr7kVBIa5UphlvWXa12w1YmUwJu7YndnJGIavLWeNCVc7Z\nEo+nS8Wr/4QWicatIWZXpVaEOPhRoeplQDxNWg5b/Q26rYoVd7PrCmRs7sVcH79X\nzGONeH1PAgMBAAECggEAANb3Wtwl07pCjRrMvc7WbC0xYIn82yu8/g2qtjkYUJcU\nia5lQbYN7RGCS85Oc/tkq48xQEG5JQWNH8b918jDEMTrFab0aUEyYcru1q9L8PL6\nYHaNgZSrMrDcHcS8h0QOXNRJT5jeGkiHJaTR0irvB526tqF3knbK9yW22KTfycUe\na0Z9voKn5xRk1DCbHi/nk2EpT7xnjeQeLFaTIRXbS68omkr4YGhwWm5OizoyEGZu\nW0Zum5BkQyMr6kor3wdxOTG97ske2rcyvvHi+ErnwL0xBv0qY0Dhe8DpuXpDezqw\no72yY8h31Fu84i7sAj24YuE5Df8DozItFXQpkgbQ6QKBgQDPrufhvIFm2S/MzBdW\nH8JxY7CJlJPyxOvc1NIl9RczQGAQR90kx52cgIcuIGEG6/wJ/xnGfMmW40F0DnQ+\nN+oLgB9SFxeLkRb7s9Z/8N3uIN8JJFYcerEOiRQeN2BXEEWJ7bUThNtsVrAcKoUh\nELsDmnHW/3V+GKwhd0vpk842+wKBgQDf4PGLG9PTE5tlAoyHFodJRd2RhTJQkwsU\nMDNjLJ+KecLv+Nl+QiJhoflG1ccqtSFlBSCG067CDQ5LV0xm3mLJ7pfJoMgjcq31\nqjEmX4Ls91GuVOPtbwst3yFKjsHaSoKB5fBvWRcKFpBUezM7Qcw2JP3+dQT+bQIq\ncMTkRWDSvQKBgQDOdCQFDjxg/lR7NQOZ1PaZe61aBz5P3pxNqa7ClvMaOsuEQ7w9\nvMYcdtRq8TsjA2JImbSI0TIg8gb2FQxPcYwTJKl+FICOeIwtaSg5hTtJZpnxX5LO\nutTaC0DZjNkTk5RdOdWA8tihyUdGqKoxJY2TVmwGe2rUEDjFB++J4inkEwKBgB6V\ng0nmtkxanFrzOzFlMXwgEEHF+Xaqb9QFNa/xs6XeNnREAapO7JV75Cr6H2hFMFe1\nmJjyqCgYUoCWX3iaHtLJRnEkBtNY4kzyQB6m46LtsnnnXO/dwKA2oDyoPfFNRoDq\nYatEd3JIXNU9s2T/+x7WdOBjKhh72dTkbPFmTPDdAoGAU6rlPBevqOFdObYxdPq8\nEQWu44xqky3Mf5sBpOwtu6rqCYuziLiN7K4sjN5GD5mb1cEU+oS92ZiNcUQ7MFXk\n8yTYZ7U0VcXyAcpYreWwE8thmb0BohJBr+Mp3wLTx32x0HKdO6vpUa0d35LUTUmM\nRrKmPK/msHKK/sVHiL+NFqo=\n-----END PRIVATE KEY-----\n" "certificate_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1n9j9C5Bes1nd\nqACDckERauxXVNKCnUlUM1buGBx1xc+j2e2Ar23wUJJuWBY18VfT8yqfqVDktO2w\nrbmvZvLuPmXePOKbIKS+XXh+2NG9L5bDG9rwGFCRXnbQj+GWCdMfzx14+CR1IHge\nYz6Cv/Si2/LJPCh/CoBfM4hUQJON3lxAWrWBpdbZnKYMrxuPBRfW9OuzTbCVXToQ\noxRAHiOR9081Xn1WeoKr7kVBIa5UphlvWXa12w1YmUwJu7YndnJGIavLWeNCVc7Z\nEo+nS8Wr/4QWicatIWZXpVaEOPhRoeplQDxNWg5b/Q26rYoVd7PrCmRs7sVcH79X\nzGONeH1PAgMBAAECggEAANb3Wtwl07pCjRrMvc7WbC0xYIn82yu8/g2qtjkYUJcU\nia5lQbYN7RGCS85Oc/tkq48xQEG5JQWNH8b918jDEMTrFab0aUEyYcru1q9L8PL6\nYHaNgZSrMrDcHcS8h0QOXNRJT5jeGkiHJaTR0irvB526tqF3knbK9yW22KTfycUe\na0Z9voKn5xRk1DCbHi/nk2EpT7xnjeQeLFaTIRXbS68omkr4YGhwWm5OizoyEGZu\nW0Zum5BkQyMr6kor3wdxOTG97ske2rcyvvHi+ErnwL0xBv0qY0Dhe8DpuXpDezqw\no72yY8h31Fu84i7sAj24YuE5Df8DozItFXQpkgbQ6QKBgQDPrufhvIFm2S/MzBdW\nH8JxY7CJlJPyxOvc1NIl9RczQGAQR90kx52cgIcuIGEG6/wJ/xnGfMmW40F0DnQ+\nN+oLgB9SFxeLkRb7s9Z/8N3uIN8JJFYcerEOiRQeN2BXEEWJ7bUThNtsVrAcKoUh\nELsDmnHW/3V+GKwhd0vpk842+wKBgQDf4PGLG9PTE5tlAoyHFodJRd2RhTJQkwsU\nMDNjLJ+KecLv+Nl+QiJhoflG1ccqtSFlBSCG067CDQ5LV0xm3mLJ7pfJoMgjcq31\nqjEmX4Ls91GuVOPtbwst3yFKjsHaSoKB5fBvWRcKFpBUezM7Qcw2JP3+dQT+bQIq\ncMTkRWDSvQKBgQDOdCQFDjxg/lR7NQOZ1PaZe61aBz5P3pxNqa7ClvMaOsuEQ7w9\nvMYcdtRq8TsjA2JImbSI0TIg8gb2FQxPcYwTJKl+FICOeIwtaSg5hTtJZpnxX5LO\nutTaC0DZjNkTk5RdOdWA8tihyUdGqKoxJY2TVmwGe2rUEDjFB++J4inkEwKBgB6V\ng0nmtkxanFrzOzFlMXwgEEHF+Xaqb9QFNa/xs6XeNnREAapO7JV75Cr6H2hFMFe1\nmJjyqCgYUoCWX3iaHtLJRnEkBtNY4kzyQB6m46LtsnnnXO/dwKA2oDyoPfFNRoDq\nYatEd3JIXNU9s2T/+x7WdOBjKhh72dTkbPFmTPDdAoGAU6rlPBevqOFdObYxdPq8\nEQWu44xqky3Mf5sBpOwtu6rqCYuziLiN7K4sjN5GD5mb1cEU+oS92ZiNcUQ7MFXk\n8yTYZ7U0VcXyAcpYreWwE8thmb0BohJBr+Mp3wLTx32x0HKdO6vpUa0d35LUTUmM\nRrKmPK/msHKK/sVHiL+NFqo=\n-----END PRIVATE KEY-----\n"
} }
} }
},
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["certificate", "certificate_key"],
"properties": {
"certificate": {
"type": "string",
"minLength": 1
},
"certificate_key": {
"type": "string",
"minLength": 1
},
"intermediate_certificate": {
"type": "string",
"minLength": 1
}
}
} }
} }
} }

13
scripts/cypress-dev Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash -e
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$DIR/.common.sh"
# Ensure docker-compose exists
if hash docker-compose 2>/dev/null; then
cd "${DIR}/.."
rm -rf "$DIR/../test/results"
docker-compose up --build cypress
else
echo -e "${RED} docker-compose command is not available${RESET}"
fi

View file

@ -0,0 +1,61 @@
/// <reference types="cypress" />
describe('Full Certificate Provisions', () => {
let token;
before(() => {
cy.getToken().then((tok) => {
token = tok;
});
});
it.only('Should be able to create new http certificate', function() {
cy.task('backendApiPost', {
token: token,
path: '/api/nginx/certificates',
data: {
domain_names: [
'website1.example.com'
],
meta: {
letsencrypt_email: 'admin@example.com',
letsencrypt_agree: true,
dns_challenge: false
},
provider: 'letsencrypt'
}
}).then((data) => {
cy.validateSwaggerSchema('post', 201, '/nginx/certificates', data);
expect(data).to.have.property('id');
expect(data.id).to.be.greaterThan(0);
expect(data.provider).to.be.equal('letsencrypt');
});
});
it('Should be able to create new DNS certificate with Powerdns', function() {
cy.task('backendApiPost', {
token: token,
path: '/api/certificates',
data: {
domain_names: [
'website2.example.com'
],
meta: {
letsencrypt_email: "admin@example.com",
dns_challenge: true,
dns_provider: 'powerdns',
dns_provider_credentials: 'dns_powerdns_api_url = http://ns1.pdns:8081\r\ndns_powerdns_api_key = npm',
letsencrypt_agree: true
},
provider: 'letsencrypt'
}
}).then((data) => {
cy.validateSwaggerSchema('post', 201, '/nginx/certificates', data);
expect(data).to.have.property('id');
expect(data.id).to.be.greaterThan(0);
expect(data.provider).to.be.equal('letsencrypt');
expect(data.meta.dns_provider).to.be.equal('powerdns');
});
});
});