diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 901128d..34b8fdf 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -209,6 +209,7 @@ const internalCertificate = { .patchAndFetchById(certificate.id, { expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss') }) + .then(utils.omitRow(omissions())) .then((saved_row) => { // Add cert data for audit log saved_row.meta = _.assign({}, saved_row.meta, { @@ -732,29 +733,29 @@ const internalCertificate = { return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout') .then((result) => { + // Examples: + // subject=CN = *.jc21.com // subject=CN = something.example.com const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim; const match = regex.exec(result); - - if (typeof match[1] === 'undefined') { - throw new error.ValidationError('Could not determine subject from certificate: ' + result); + if (match && typeof match[1] !== 'undefined') { + certData['cn'] = match[1]; } - - certData['cn'] = match[1]; }) .then(() => { return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout'); }) + .then((result) => { + // Examples: // 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 match = regex.exec(result); - - if (typeof match[1] === 'undefined') { - throw new error.ValidationError('Could not determine issuer from certificate: ' + result); + if (match && typeof match[1] !== 'undefined') { + certData['issuer'] = match[1]; } - - certData['issuer'] = match[1]; }) .then(() => { return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout'); @@ -829,16 +830,16 @@ const internalCertificate = { requestLetsEncryptSsl: (certificate) => { logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); - const cmd = certbotCommand + ' certonly ' + - '--config "' + letsencryptConfig + '" ' + + const cmd = `${certbotCommand} certonly ` + + `--config '${letsencryptConfig}' ` + '--work-dir "/tmp/letsencrypt-lib" ' + '--logs-dir "/tmp/letsencrypt-log" ' + - '--cert-name "npm-' + certificate.id + '" ' + + `--cert-name "npm-${certificate.id}" ` + '--agree-tos ' + '--authenticator webroot ' + - '--email "' + certificate.meta.letsencrypt_email + '" ' + + `--email '${certificate.meta.letsencrypt_email}' ` + '--preferred-challenges "dns,http" ' + - '--domains "' + certificate.domain_names.join(',') + '" ' + + `--domains "${certificate.domain_names.join(',')}" ` + (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') + (letsencryptStaging && letsencryptServer === null ? '--staging ' : ''); @@ -871,25 +872,26 @@ const internalCertificate = { const hasConfigArg = certificate.meta.dns_provider !== 'route53'; let mainCmd = certbotCommand + ' certonly ' + - '--config "' + letsencryptConfig + '" ' + + `--config '${letsencryptConfig}' ` + '--work-dir "/tmp/letsencrypt-lib" ' + '--logs-dir "/tmp/letsencrypt-log" ' + - '--cert-name "npm-' + certificate.id + '" ' + + `--cert-name 'npm-${certificate.id}' ` + '--agree-tos ' + - '--email "' + certificate.meta.letsencrypt_email + '" ' + - '--domains "' + certificate.domain_names.join(',') + '" ' + - '--authenticator ' + dnsPlugin.full_plugin_name + ' ' + + `--email '${certificate.meta.letsencrypt_email}' ` + + `--domains '${certificate.domain_names.join(',')}' ` + + `--authenticator '${dnsPlugin.full_plugin_name}' ` + ( hasConfigArg - ? '--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"' + ? `--${dnsPlugin.full_plugin_name}-credentials '${credentialsLocation}' ` : '' ) + ( 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 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(', ')); const cmd = certbotCommand + ' renew --force-renewal ' + - '--config "' + letsencryptConfig + '" ' + + `--config '${letsencryptConfig}' ` + '--work-dir "/tmp/letsencrypt-lib" ' + '--logs-dir "/tmp/letsencrypt-log" ' + - '--cert-name "npm-' + certificate.id + '" ' + + `--cert-name 'npm-${certificate.id}' ` + '--preferred-challenges "dns,http" ' + '--no-random-sleep-on-renew ' + '--disable-hook-validation ' + - (letsencryptStaging ? '--staging' : ''); + (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') + + (letsencryptStaging && letsencryptServer === null ? '--staging ' : ''); 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(', ')}`); let mainCmd = certbotCommand + ' renew --force-renewal ' + - '--config "' + letsencryptConfig + '" ' + + `--config "${letsencryptConfig}" ` + '--work-dir "/tmp/letsencrypt-lib" ' + '--logs-dir "/tmp/letsencrypt-log" ' + - '--cert-name "npm-' + certificate.id + '" ' + + `--cert-name 'npm-${certificate.id}' ` + '--disable-hook-validation ' + '--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 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(', ')); const mainCmd = certbotCommand + ' revoke ' + - '--config "' + letsencryptConfig + '" ' + + `--config '${letsencryptConfig}' ` + '--work-dir "/tmp/letsencrypt-lib" ' + '--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 ' + - (letsencryptStaging ? '--staging' : ''); + (letsencryptServer !== null ? `--server '${letsencryptServer}' ` : '') + + (letsencryptStaging && letsencryptServer === null ? '--staging ' : ''); // Don't fail command if file does not exist const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`; diff --git a/backend/schema/components/certificate-object.json b/backend/schema/components/certificate-object.json index a4e8e1f..b75dcf6 100644 --- a/backend/schema/components/certificate-object.json +++ b/backend/schema/components/certificate-object.json @@ -45,11 +45,13 @@ "type": "object", "additionalProperties": false, "properties": { - "letsencrypt_email": { - "type": "string" + "certificate": { + "type": "string", + "minLength": 1 }, - "letsencrypt_agree": { - "type": "boolean" + "certificate_key": { + "type": "string", + "minLength": 1 }, "dns_challenge": { "type": "boolean" @@ -60,17 +62,18 @@ "dns_provider_credentials": { "type": "string" }, + "letsencrypt_agree": { + "type": "boolean" + }, + "letsencrypt_certificate": { + "type": "object" + }, + "letsencrypt_email": { + "type": "string" + }, "propagation_seconds": { "type": "integer", "minimum": 0 - }, - "certificate": { - "type": "string", - "minLength": 1 - }, - "certificate_key": { - "type": "string", - "minLength": 1 } } } diff --git a/test/cypress/e2e/api/FullCertProvision.cy.js b/test/cypress/e2e/api/FullCertProvision.cy.js new file mode 100644 index 0000000..93cacce --- /dev/null +++ b/test/cypress/e2e/api/FullCertProvision.cy.js @@ -0,0 +1,61 @@ +/// + +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'); + }); + }); + +}); diff --git a/test/cypress/plugins/backendApi/client.js b/test/cypress/plugins/backendApi/client.js index ba28e3a..c10653e 100644 --- a/test/cypress/plugins/backendApi/client.js +++ b/test/cypress/plugins/backendApi/client.js @@ -7,7 +7,7 @@ const BackendApi = function(config, token) { this.axios = axios.create({ baseURL: config.baseUrl, - timeout: 5000, + timeout: 60000, }); };