From 08f87be1f65782d347de78a99b96e0f10ec5874c Mon Sep 17 00:00:00 2001 From: jash Date: Mon, 5 Nov 2018 21:58:31 +0100 Subject: [PATCH] cert creation now takes CNs as argument for which the cert is valid --- .../generators/app/help.json | 1 + .../generators/app/index.js | 11 +++- .../generators/app/lib/cert.js | 64 ++++++++++++++++++- .../app/prompters/010_gatekeeper.js | 8 +-- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/install/generator-cyphernode/generators/app/help.json b/install/generator-cyphernode/generators/app/help.json index 19d36d4..225d3d5 100644 --- a/install/generator-cyphernode/generators/app/help.json +++ b/install/generator-cyphernode/generators/app/help.json @@ -15,6 +15,7 @@ "gatekeeper_apiproperties": "** gatekeeper_apiproperties **", "gatekeeper_sslcert": "** gatekeeper_sslcert **", "gatekeeper_sslkey": "** gatekeeper_sslkey **", + "gatekeeper_cns": "** gatekeeper_cns **", "bitcoin_mode": "** bitcoin_mode **", "bitcoin_node_ip": "** bitcoin_node_ip **", "bitcoin_rpcuser": "** bitcoin_rpcuser **", diff --git a/install/generator-cyphernode/generators/app/index.js b/install/generator-cyphernode/generators/app/index.js index 5e5910f..7c572e9 100644 --- a/install/generator-cyphernode/generators/app/index.js +++ b/install/generator-cyphernode/generators/app/index.js @@ -193,6 +193,7 @@ module.exports = class extends Generator { // save gatekeeper key password to check if it changed this.gatekeeper_clientkeyspassword = this.props.gatekeeper_clientkeyspassword; + this.gatekeeper_cns = this.props.gatekeeper_cns; let r = await this.prompt([{ type: 'confirm', @@ -250,13 +251,16 @@ module.exports = class extends Generator { } } - if( this.props.gatekeeper_recreatecert || + const oldCNS = (this.gatekeeper_cns||'').split(',').map(e=>e.trim().toLowerCase()).filter(e=>!!e); + const newCNS = (this.props.gatekeeper_cns||'').split(',').map(e=>e.trim().toLowerCase()).filter(e=>!!e); + + if( oldCNS.sort().join('') !== newCNS.sort().join('') || !this.props.gatekeeper_sslcert || !this.props.gatekeeper_sslkey ) { const cert = new Cert(); console.log(chalk.bold.green( '☕ Generating gatekeeper cert. This may take a while ☕' )); try { - const result = await cert.create(); + const result = await cert.create(newCNS); if( result.code === 0 ) { this.props.gatekeeper_sslkey = result.key.toString(); this.props.gatekeeper_sslcert = result.cert.toString(); @@ -268,7 +272,7 @@ module.exports = class extends Generator { } } - delete this.props.gatekeeper_recreatecert; + delete this.props.gatekeeper_recreatekeys; } @@ -344,6 +348,7 @@ module.exports = class extends Generator { gatekeeper_keys: { configEntries: [], clientInformation: [] }, gatekeeper_sslcert: '', gatekeeper_sslkey: '', + gatekeeper_cns: '', proxy_datapath: '', lightning_implementation: 'c-lightning', lightning_datapath: '', diff --git a/install/generator-cyphernode/generators/app/lib/cert.js b/install/generator-cyphernode/generators/app/lib/cert.js index 7108dab..01fdde4 100644 --- a/install/generator-cyphernode/generators/app/lib/cert.js +++ b/install/generator-cyphernode/generators/app/lib/cert.js @@ -3,32 +3,89 @@ const spawn = require('child_process').spawn; const defaultArgs = ['req', '-x509', '-newkey', 'rsa:4096', '-nodes']; const path = require('path'); const tmp = require('tmp'); +const validator = require('validator'); + +const confTmpl = ` +[req] +distinguished_name = req_distinguished_name +x509_extensions = v3_ca +prompt = no +[req_distinguished_name] +CN = %PRIMARY_CN% +[v3_ca] +subjectAltName = @alt_names +[alt_names] +%ALT_DOMAINS% +%ALT_IPS% +`; + +const domainTmpl = 'DNS.%#% = %DOMAIN%'; +const ipTmpl = 'IP.%#% = %IP%' module.exports = class Cert { constructor( options ) { options = options || {}; - this.args = options.args || { subj: '/CN=localhost', days: 3650 }; + this.args = options.args || { days: 3650 }; } - async create() { + buildConfig( cns ) { + + let ips = []; + let domains = []; + + for( let cn of cns ) { + if( validator.isIP(cn) ) { + ips.push( cn ); + } else { + domains.push( cn ); + } + } + + let conf = confTmpl; + + if( !domains.length ) { + domains.push('localhost'); + } + + conf = conf.replace( '%PRIMARY_CN%', domains[0] ) + + let domainCount = 0; + domains = domains.map( d => domainTmpl.replace( '%#%', ++domainCount ).replace('%DOMAIN%', d) ); + conf = conf.replace( '%ALT_DOMAINS%', domains.join('\n') || '' ) + + let ipCount = 0; + ips = ips.map( ip => ipTmpl.replace( '%#%', ++ipCount ).replace('%IP%', ip) ); + conf = conf.replace( '%ALT_IPS%', ips.join('\n') || '' ) + + return conf; + } + + async create( cns ) { + cns = cns || []; let args = defaultArgs.slice(); const certFileTmp = tmp.fileSync(); const keyFileTmp = tmp.fileSync(); + const confFileTmp = tmp.fileSync(); args.push( '-out' ); args.push( certFileTmp.name ); args.push( '-keyout' ); args.push( keyFileTmp.name ); + args.push( '-config' ); + args.push( confFileTmp.name ); for( let k in this.args ) { args.push( '-'+k); args.push( this.args[k] ); } - const openssl = spawn('openssl', args, { stdio: ['ignore','ignore','ignore'] } ); + const conf = this.buildConfig( cns ); + fs.writeFileSync( confFileTmp.name, conf ); + + const openssl = spawn('openssl', args, { stdio: ['ignore', 'ignore', 'ignore'] } ); let code = await new Promise( function(resolve, reject) { openssl.on('exit', (code) => { @@ -41,6 +98,7 @@ module.exports = class Cert { certFileTmp.removeCallback(); keyFileTmp.removeCallback(); + confFileTmp.removeCallback(); return { code: code, diff --git a/install/generator-cyphernode/generators/app/prompters/010_gatekeeper.js b/install/generator-cyphernode/generators/app/prompters/010_gatekeeper.js index 6f4b5ae..b043118 100644 --- a/install/generator-cyphernode/generators/app/prompters/010_gatekeeper.js +++ b/install/generator-cyphernode/generators/app/prompters/010_gatekeeper.js @@ -66,10 +66,10 @@ module.exports = { }, { when: function() { return hasCert( utils.props ); }, - type: 'confirm', - name: 'gatekeeper_recreatecert', - default: false, - message: prefix()+'Recreate gatekeeper ssl cert?'+utils._getHelp('gatekeeper_recreatecert') + type: 'input', + name: 'gatekeeper_cns', + default: utils._getDefault( 'gatekeeper_cns' ), + message: prefix()+'Gatekeeper cert CNS (ips, domains, wildcard domains seperated by comma)?'+utils._getHelp('gatekeeper_cns') }, { type: 'confirm',