Skip to content

Commit

Permalink
Merge pull request #31 from zowe/keyring-support
Browse files Browse the repository at this point in the history
add keyring support
  • Loading branch information
NakulManchanda authored Jul 13, 2020
2 parents be54ab2 + 6a07e19 commit 9eb6273
Show file tree
Hide file tree
Showing 17 changed files with 284 additions and 58 deletions.
6 changes: 4 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"program": "${workspaceFolder}\\src\\index.js",
//"args": ["-s", "jes", "-b", "/ui/v1/explorer-jes", "-p", "7554", "-k", "C:\\zowe\\explorer-componentisation\\configs\\server.key", "-c", "C:\\zowe\\explorer-componentisation\\configs\\server.cert", "-x", "", "-w", "", "-f", "https://tvt5003.svl.ibm.com"],
//"args": [ "-s", "jes", "-b", "/", "-p", "9090", "-f", "https://test.zowe.org/*", "-k", "configs/server.key", "-c", "configs/server.cert" ]
"args": [ "-s", "", "-b", "/relpath", "-d", "public","-p", "9090", "-f", "https://test.zowe.org/*", "-k", "configs/server.key", "-c", "configs/server.cert" ]
//"args": [ "-s", "", "-b", "/cache", "-d", "public/cache","-p", "9090", "-f", "https://test.zowe.org/*", "-k", "configs/server.key", "-c", "configs/server.cert" ]
"args": ["-s", "jes", "-b", "/", "-d", "public", "-p", "9090", "-x", "configs/server.pfx", "-w", "pass"]
//"args": ["-s", "jes", "-b", "/", "-d", "public", "-p", "9090", "-k","configs/server.key","-c","configs/server.cert","-x","configs/server.pfx","-w","pass","-n","keyring","-o","owner","-l","label","-v"]
},
{
"type": "node",
Expand All @@ -36,7 +38,7 @@
"--timeout",
"999999",
"--colors",
"${workspaceFolder}\\test\\relpath\\test-relpath.js"
"${workspaceFolder}\\test\\https-combos\\test-https-combo.js"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
Expand Down
6 changes: 4 additions & 2 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ node('ibm-jenkins-slave-nvm') {
publishRegistry: [
email : lib.Constants.DEFAULT_LFJ_NPM_PRIVATE_REGISTRY_EMAIL,
usernamePasswordCredential : lib.Constants.DEFAULT_LFJ_NPM_PRIVATE_REGISTRY_CREDENTIAL,
]
],
// FIXME: ideally this should set to false (using default by remove this line)
ignoreAuditFailure : true,
)

// build stage is required
Expand All @@ -57,7 +59,7 @@ node('ibm-jenkins-slave-nvm') {
scannerTool : lib.Constants.DEFAULT_LFJ_SONARCLOUD_SCANNER_TOOL,
scannerServer : lib.Constants.DEFAULT_LFJ_SONARCLOUD_SERVER,
allowBranchScan : lib.Constants.DEFAULT_LFJ_SONARCLOUD_ALLOW_BRANCH,
failBuild : lib.Constants.DEFAULT_LFJ_SONARCLOUD_FAIL_BUILD
failBuild : false
)

// define we need publish stage
Expand Down
36 changes: 23 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ Provide simple HTTPS server to server Zowe Desktop Explorer plugins.
## Start Dev Server

```
npm start
// with key cert
npm start -- -s "jes" -b "/" -d "public" -p "9090" -k "configs/server.key" -c "configs/server.cert"
// with pfx pass
npm start -- -s "jes" -b "/" -d "public" -p "9090" -x "configs/server.pfx" -w "pass"
// with keyring
npm start -- -s "jes" -b "/" -d "public" -p "9090" -n "keyring" -o "keyring-owner" -l "keyring-label"
```

Then visit `https://localhost:9090` to access the test server. The default config file is `configs/config-default.json`.
Expand All @@ -19,18 +26,21 @@ $ node src/index.js -h
Usage: explorer-ui-server [options]
Options:
--version Show version number [boolean]
-s, --service service-for path [default: ""]
-b, --path base path uri
-d, --dir base dir [required] [default: "../app"]
-p, --port listening port
-k, --key server key [default: ""]
-c, --cert server cert [default: ""]
-x, --pfx server pfx [default: ""]
-w, --pass server pfx passphrase [default: ""]
-f, --csp csp whitelist ancestors frames [required]
-v, --verbose show request logs [boolean] [default: false]
-h, --help Show help [boolean]
--version Show version number [boolean]
-s, --service service-for path [default: ""]
-b, --path base path uri
-d, --dir base dir [required] [default: "../app"]
-p, --port listening port
-k, --key server key [default: ""]
-c, --cert server cert [default: ""]
-x, --pfx server pfx [default: ""]
-w, --pass server pfx passphrase [default: ""]
-n, --keyring keyring name [default: ""]
-o, --keyring-owner keyring owner id [default: ""]
-l, --keyring-label keyring certificate label [default: ""]
-f, --csp csp whitelist ancestors frames [required]
-v, --verbose show request logs [boolean] [default: false]
-h, --help Show help [boolean]
```

## Run Tests
Expand Down
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
"dependencies": {
"stdio": "0.2.7"
},
"optionalDependencies": {
"keyring_js": "~1.0.0"
},
"devDependencies": {
"abort-controller": "^3.0.0",
"axios": "^0.19.0",
Expand Down
119 changes: 101 additions & 18 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,76 @@ const fs = require('fs');
const path = require('path');
const rootDir = path.resolve(__dirname, '..');
const pkg = require('../package.json');
const { HTTPS_TYPE } = require('./utils');

const os = require('os');
let keyring_js;
try {
if (os.platform() == 'os390') {
keyring_js = require('keyring_js');
}
} catch (e) {
process.stdout.write('Could not load zcrypto library, SAF keyrings will be unavailable\n');
}

function validateParams (param) {

function validateParams (params) {

let isValid = true;

const serviceFor=param.service;
const serviceFor=params.service;

if((param.path==='' || !param.path) && isValid) {
if((params.path==='' || !params.path) && isValid) {
isValid = false;
process.stderr.write(`[${serviceFor}] paths configuration is missing\n`);
}

if((param.port==='' || !param.port) && isValid) {
if((params.port==='' || !params.port) && isValid) {
isValid = false;
process.stderr.write(`[${serviceFor}] port configuration is missing\n`);
}

if( (param.key==='' && param.cert==='' && param.pfx==='' && param.pass==='') && isValid) {
if( (params.key==='' && params.cert==='' && params.pfx==='' && params.pass==='' && params['keyring'] ==='' && params['keyring-owner']==='' && params['keyring-label']==='') && isValid) {
isValid = false;
process.stderr.write(`[${serviceFor}] https configuration is missing\n`);
}

if( ( (param.key==='' && param.cert>'') || (param.key>'' && param.cert==='')
|| (param.pfx==='' && param.pass>'' && param.key==='' && param.cert==='')
|| (param.pfx==='' && param.pass>'' && !(param.key>'' && param.cert>''))
|| (param.pfx>'' && param.pass==='') ) && isValid) {
if( whichHttpsType(params)===0 && isValid) {
isValid = false;
process.stderr.write(`[${serviceFor}] https configuration is missing\n`);
}

if(!isValid) {
process.stderr.write(`[${serviceFor}] is failed to start, error:\n`);
param.printHelp();
params.printHelp();
process.exit(1);
return false;
}

return true;
}

function whichHttpsType(params) {
if(params.key>'' && params.cert>'') {
return HTTPS_TYPE.KEY_CERT;
}

if(params.pfx>'' && params.pass>'') {
return HTTPS_TYPE.PFX_PASS;
}

if(params.keyring>'' && params['keyring-owner']>'' && params['keyring-label']>'') {
return HTTPS_TYPE.KEYRING;
}

return 0;
}

function parseCsp(config) {
if(config && config.csp && config.csp['frame-ancestors']) {
const frames=config.csp['frame-ancestors'];
if(frames.length>0 && frames[0]>'') {
config.csp['frame-ancestors'] = config.csp['frame-ancestors'][0].split(',');
config.csp['frame-ancestors'] = frames[0].split(',');
} else {
config.csp['frame-ancestors'] = [];
}
Expand All @@ -65,14 +90,66 @@ function parseCsp(config) {
}

function loadHttpsCerts(config) {
// load https certs file content
// load https type
if (config && config.https) {
['key', 'cert', 'pfx'].forEach(key => {
if (config.https[key]) {
let file = config.https[key];
config.https[key] = fs.readFileSync(file);
}
if(config.https.type === HTTPS_TYPE.KEY_CERT) {
config = loadKeyCerts(config);
} else if(config.https.type === HTTPS_TYPE.PFX_PASS) {
config = loadPfx(config);
} else if(config.https.type === HTTPS_TYPE.KEYRING) {
config = loadKeyringCerts(config);
}
}
return config;
}

function loadKeyCerts(config) {
const serviceFor = config.serviceFor;
try {
['key', 'cert'].forEach(key => {
let filePath = config.https[key];
config.https[key] = fs.readFileSync(filePath);
});
} catch(err) {
process.stderr.write(`[${serviceFor}] exception thrown when reading key cert files no such file or directory\n`);
process.exit(1);
}
return config;
}

function loadPfx(config) {
const serviceFor = config.serviceFor;
try {
let filePath = config.https['pfx'];
config.https['pfx'] = fs.readFileSync(filePath);
} catch(err) {
process.stderr.write(`[${serviceFor}] exception thrown when reading pfx no such file or directory\n`);
process.exit(1);
}
return config;
}

function loadKeyringCerts(config) {
const serviceFor = config.serviceFor;

if (keyring_js) {
try {
const keyringData = keyring_js.getPemEncodedData(config['keyring-owner'], config['keyring'], config['keyring-label']);
config.https.cert = keyringData.certificate;
config.https.key = keyringData.key;
} catch (err) {
process.stderr.write(`[${serviceFor}] exception thrown when reading SAF keyring\n`);
process.stderr.write(`${err}\n\n`);
process.exit(1);
}
} else {
process.stderr.write(`[${serviceFor}] cannot load SAF keyring due to missing keyring_js library\n`);
process.exit(1);
}

if(config.https.key === '' && config.https.cert === ''){
process.stderr.write(`[${serviceFor}] failed to process keyring\n`);
process.exit(1);
}
return config;
}
Expand All @@ -99,11 +176,13 @@ function loadPackageMeta(config) {
function loadParams(params) {

if(params.verbose) {
process.stdout.write(`[args]:${JSON.stringify(params)}\n`);
process.stdout.write(`[${params.service}]: args ${JSON.stringify(params)}\n`);
}

validateParams(params);

const httpType = whichHttpsType(params);

const paramConfig = {
'service-for': params.service,
'paths': [{
Expand All @@ -112,10 +191,14 @@ function loadParams(params) {
}],
'port': params.port,
'https': {
'type': httpType,
'key': params.key,
'cert': params.cert,
'pfx': params.pfx,
'passphrase': params.pass,
'keyring': params.keyring,
'keyring-owner': params['keyring-owner'],
'keyring-label': params['keyring-label']
},
'csp': {
'frame-ancestors': [params.csp]
Expand Down
23 changes: 15 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

// load command line params
const stdio = require('stdio');
const { httpTypeToString } = require('./utils');

const params = stdio.getopt({
'service': {key: 's', args:1, description: 'service-for path', default:'', type: 'string'},
'path': {key: 'b', args:1, description: 'base path uri', default:''},
Expand All @@ -22,21 +24,26 @@ const params = stdio.getopt({
'pfx': {key: 'x', args:1, description: 'server pfx', default:''},
'pass': {key: 'w', args:1, description: 'server pfx passphrase', default:''},
'csp': {key: 'f', args:1, description: 'csp whitelist ancestors frames', default:''},
'keyring': {key: 'n', args:1, description: 'keyring name', default:''},
'keyring-owner': {key: 'o', args:1, description: 'keyring owner id', default:''},
'keyring-label': {key: 'l', args:1, description: 'keyring certificate label', default:''},
'verbose': {key: 'v', default:false}
});
const serviceFor = params.service;

// load config
let config;
try {
config = require('./config')(params);
process.stdout.write(`[rootDir]:${config.rootDir}\n`);
process.stdout.write(`[version]:${config.version}\n`);
process.stdout.write(`[script name]:${config.scriptName}\n`);
process.stdout.write(`[serviceFor]:${config.serviceFor}\n`);
process.stdout.write(`[paths]:${JSON.stringify(config.paths)}\n`);
} catch (err) {
process.stderr.write('failed to process config\n');
process.stderr.write(`${err}\n\n`);
process.stdout.write(`[${serviceFor}] rootDir ${config.rootDir}\n`);
process.stdout.write(`[${serviceFor}] version ${config.version}\n`);
process.stdout.write(`[${serviceFor}] script name ${config.scriptName}\n`);
process.stdout.write(`[${serviceFor}] paths ${JSON.stringify(config.paths)}\n`);
process.stdout.write(`[${serviceFor}] port ${JSON.stringify(config.port)}\n`);
process.stdout.write(`[${serviceFor}] https using ${httpTypeToString(config.https.type)}\n`);
} catch (err) {
process.stderr.write(`[${serviceFor}] failed to process config\n`);
process.stderr.write(`[${serviceFor}] ${err}\n\n`);
process.exit(1);
}

Expand Down
22 changes: 20 additions & 2 deletions src/server.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
const https = require('https');
const { constants: cryptoConstants } = require('crypto');
const { HTTPS_TYPE } = require('./utils');


function extractHttpsConfigFromConfig(config) {
let httpsConfig = {};
if(config.https.type === HTTPS_TYPE.KEY_CERT || config.https.type === HTTPS_TYPE.KEYRING) {
httpsConfig = {
key: config.https.key,
cert: config.https.cert
};
} else if(config.https.type === HTTPS_TYPE.PFX_PASS) {
httpsConfig = {
pfx: config.https.pfx,
passphrase: config.https.passphrase
};
}
return httpsConfig;
}

function createHttpsServer (config, requestHandler) {
if (!config.https ||
Expand All @@ -23,11 +41,11 @@ function createHttpsServer (config, requestHandler) {
'ECDHE-ECDSA-AES128-SHA256',
'ECDHE-ECDSA-AES256-SHA384'].join(':');

const httpsServer = https.createServer(config.https, requestHandler);
const httpsConfig = extractHttpsConfigFromConfig(config);
const httpsServer = https.createServer(httpsConfig, requestHandler);
return httpsServer;
}


module.exports = (config, requestHandler) => {
const httpsServer = createHttpsServer(config, requestHandler);
return httpsServer;
Expand Down
Loading

0 comments on commit 9eb6273

Please sign in to comment.