Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
1dbf57c2ba
6 changed files with 163 additions and 94 deletions
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
13
scripts/cypress-dev
Executable 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
|
61
test/cypress/e2e/api/FullCertProvision.cy.js
Normal file
61
test/cypress/e2e/api/FullCertProvision.cy.js
Normal 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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in a new issue