Cypress test for http and dns cert provision

This commit is contained in:
Jamie Curnow 2024-10-16 14:53:57 +10:00
parent 5bdc05878f
commit fe2d8895d6
No known key found for this signature in database
GPG Key ID: FFBB624C43388E9E
4 changed files with 114 additions and 45 deletions

View File

@ -209,6 +209,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, {
@ -732,29 +733,29 @@ const internalCertificate = {
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout') return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
.then((result) => { .then((result) => {
// Examples:
// subject=CN = *.jc21.com
// subject=CN = something.example.com // 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) => {
// 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=O = NginxProxyManager, CN = NginxProxyManager Intermediate CA","O = NginxProxyManager, CN = NginxProxyManager Intermediate CA
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');
@ -829,16 +830,16 @@ const internalCertificate = {
requestLetsEncryptSsl: (certificate) => { requestLetsEncryptSsl: (certificate) => {
logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const cmd = certbotCommand + ' certonly ' + const cmd = `${certbotCommand} certonly ` +
'--config "' + letsencryptConfig + '" ' + `--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
'--cert-name "npm-' + certificate.id + '" ' + `--cert-name "npm-${certificate.id}" ` +
'--agree-tos ' + '--agree-tos ' +
'--authenticator webroot ' + '--authenticator webroot ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' + `--email '${certificate.meta.letsencrypt_email}' ` +
'--preferred-challenges "dns,http" ' + '--preferred-challenges "dns,http" ' +
'--domains "' + certificate.domain_names.join(',') + '" ' + `--domains "${certificate.domain_names.join(',')}" ` +
(letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') + (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
(letsencryptStaging && letsencryptServer === null ? '--staging ' : ''); (letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
@ -871,25 +872,26 @@ const internalCertificate = {
const hasConfigArg = certificate.meta.dns_provider !== 'route53'; const hasConfigArg = certificate.meta.dns_provider !== 'route53';
let mainCmd = certbotCommand + ' certonly ' + let mainCmd = certbotCommand + ' certonly ' +
'--config "' + letsencryptConfig + '" ' + `--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
'--cert-name "npm-' + certificate.id + '" ' + `--cert-name 'npm-${certificate.id}' ` +
'--agree-tos ' + '--agree-tos ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' + `--email '${certificate.meta.letsencrypt_email}' ` +
'--domains "' + certificate.domain_names.join(',') + '" ' + `--domains '${certificate.domain_names.join(',')}' ` +
'--authenticator ' + dnsPlugin.full_plugin_name + ' ' + `--authenticator '${dnsPlugin.full_plugin_name}' ` +
( (
hasConfigArg hasConfigArg
? '--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"' ? `--${dnsPlugin.full_plugin_name}-credentials '${credentialsLocation}' `
: '' : ''
) + ) +
( (
certificate.meta.propagation_seconds !== undefined certificate.meta.propagation_seconds !== undefined
? ' --' + dnsPlugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds ? `--${dnsPlugin.full_plugin_name}-propagation-seconds '${certificate.meta.propagation_seconds}' `
: '' : ''
) + ) +
(letsencryptStaging ? ' --staging' : ''); (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
// Prepend the path to the credentials file as an environment variable // Prepend the path to the credentials file as an environment variable
if (certificate.meta.dns_provider === 'route53') { if (certificate.meta.dns_provider === 'route53') {
@ -966,14 +968,15 @@ const internalCertificate = {
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const cmd = certbotCommand + ' renew --force-renewal ' + const cmd = certbotCommand + ' renew --force-renewal ' +
'--config "' + letsencryptConfig + '" ' + `--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
'--cert-name "npm-' + certificate.id + '" ' + `--cert-name 'npm-${certificate.id}' ` +
'--preferred-challenges "dns,http" ' + '--preferred-challenges "dns,http" ' +
'--no-random-sleep-on-renew ' + '--no-random-sleep-on-renew ' +
'--disable-hook-validation ' + '--disable-hook-validation ' +
(letsencryptStaging ? '--staging' : ''); (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
logger.info('Command:', cmd); logger.info('Command:', cmd);
@ -998,13 +1001,14 @@ const internalCertificate = {
logger.info(`Renewing Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); logger.info(`Renewing Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
let mainCmd = certbotCommand + ' renew --force-renewal ' + let mainCmd = certbotCommand + ' renew --force-renewal ' +
'--config "' + letsencryptConfig + '" ' + `--config "${letsencryptConfig}" ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
'--cert-name "npm-' + certificate.id + '" ' + `--cert-name 'npm-${certificate.id}' ` +
'--disable-hook-validation ' + '--disable-hook-validation ' +
'--no-random-sleep-on-renew ' + '--no-random-sleep-on-renew ' +
(letsencryptStaging ? ' --staging' : ''); (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
// Prepend the path to the credentials file as an environment variable // Prepend the path to the credentials file as an environment variable
if (certificate.meta.dns_provider === 'route53') { if (certificate.meta.dns_provider === 'route53') {
@ -1030,12 +1034,13 @@ const internalCertificate = {
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const mainCmd = certbotCommand + ' revoke ' + const mainCmd = certbotCommand + ' revoke ' +
'--config "' + letsencryptConfig + '" ' + `--config '${letsencryptConfig}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' + `--cert-path '/etc/letsencrypt/live/npm-${certificate.id}/fullchain.pem' ` +
'--delete-after-revoke ' + '--delete-after-revoke ' +
(letsencryptStaging ? '--staging' : ''); (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') +
(letsencryptStaging && letsencryptServer === null ? '--staging ' : '');
// Don't fail command if file does not exist // Don't fail command if file does not exist
const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`; const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;

View File

@ -45,11 +45,13 @@
"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"
@ -60,17 +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": {
"type": "integer", "type": "integer",
"minimum": 0 "minimum": 0
},
"certificate": {
"type": "string",
"minLength": 1
},
"certificate_key": {
"type": "string",
"minLength": 1
} }
} }
} }

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');
});
});
});

View File

@ -7,7 +7,7 @@ const BackendApi = function(config, token) {
this.axios = axios.create({ this.axios = axios.create({
baseURL: config.baseUrl, baseURL: config.baseUrl,
timeout: 5000, timeout: 60000,
}); });
}; };