From 1a5ab9e32040f9058ca168210afa33c49f39767d Mon Sep 17 00:00:00 2001 From: 1000TurquoisePogs Date: Tue, 12 Dec 2023 06:09:45 -0500 Subject: [PATCH 1/3] Fix hostname versus gatewayHostname in various places. Fix missing 40W message. Add message ID for checkAgent warning. Fix checkAgent for when multiple discovery servers are present. Signed-off-by: 1000TurquoisePogs --- CHANGELOG.md | 1 + lib/apiml.js | 63 +++++++++++++++------------- lib/assets/i18n/log/messages_en.json | 1 + lib/index.js | 5 +-- lib/webapp.js | 3 +- plugins/sso-auth/lib/apimlHandler.js | 6 +-- plugins/sso-auth/lib/ssoAuth.js | 2 +- 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aac5094..2151bb13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This repo is part of the app-server Zowe Component, and the change logs here may ## 2.14.0 - Bugfix: App-server would not correctly detect when it was running in a high-availability configuration environment. +- Bugfix: App-server could not load when multiple discovery servers were present and the app-server was unable to reach the first one specified. Now, the app-server will iterate through the list of servers until an accessible one is reached. ## 2.13.0 - Added support for using zowe.network and components.app-server.zowe.network to set listener IP and TLS properties including max and min version, ciphers, and ECDH curves. (#511) diff --git a/lib/apiml.js b/lib/apiml.js index 7b71082b..95c51059 100644 --- a/lib/apiml.js +++ b/lib/apiml.js @@ -77,9 +77,9 @@ const MEDIATION_LAYER_INSTANCE_DEFAULTS = (zluxProto, zluxHostname, zluxPort) => } }}; -function ApimlConnector({ hostName, port, isHttps, discoveryHost, +function ApimlConnector({ hostName, port, isHttps, discoveryUrls, discoveryPort, tlsOptions, eurekaOverrides }) { - Object.assign(this, { hostName, port, isHttps, discoveryHost, + Object.assign(this, { hostName, port, isHttps, discoveryUrls, discoveryPort, tlsOptions, eurekaOverrides }); this.vipAddress = hostName; } @@ -110,23 +110,13 @@ ApimlConnector.prototype = { const end = Date.now() + timer; return new Promise((resolve, reject) => { - const options = Object.assign({ - host: this.discoveryHost, - port: this.discoveryPort, - method: 'GET', - path: `/eureka/apps/${serviceName}`, - headers: {'accept':'application/json'} - }, this.tlsOptions); - - if (!this.tlsOptions.rejectUnauthorized) { - //Keeping these certs causes an openssl error 46, unknown cert error in a dev environment - delete options.cert; - delete options.key; - } //else, apiml expects a cert and will give a 403. + const optionsArray = getRequestOptionsArray('GET', `/eureka/apps/${serviceName}`); + let optionsIndex = 0; const issueRequest = () => { + const options = optionsArray[optionsIndex]; if (Date.now() > end) { - log.warn(`ZWED0045`, this.discoveryHost, this.discoveryPort); + log.warn(`ZWED0045W`, this.discoveryHost, this.discoveryPort); return reject(new Error(`Call timeout when fetching agent status from APIML`)); } @@ -156,7 +146,9 @@ ApimlConnector.prototype = { reject(new Error(`Call timeout when fetching agent status from APIML`)); }); req.on('error', (error) => { - log.warn("APIML query error:", error.message); + log.warn("ZWES0180W", options.host, options.port, error.message); + // + optionsIndex = (optionsIndex+1) % optionsArray.length; setTimeout(issueRequest, AGENT_CHECK_RECONNECT_DELAY); }); req.end(); @@ -249,8 +241,7 @@ ApimlConnector.prototype = { } log.debug("ZWED0144I", JSON.stringify(zluxProxyServerInstanceConfig, null, 2)); //log.debug("zluxProxyServerInstanceConfig: " //+ JSON.stringify(zluxProxyServerInstanceConfig, null, 2)) - const defaultUrl = `https://${this.discoveryHost}:${this.discoveryPort}/eureka/apps`; - const serviceUrls = this.getServiceUrls(defaultUrl); + const serviceUrls = this.getServiceUrls(); zluxProxyServerInstanceConfig.eureka.serviceUrls = { default: serviceUrls }; log.info(`ZWED0020I`, serviceUrls.join(',')); //log.info(`Registering at ${url}...`); log.debug("ZWED0145I", JSON.stringify(zluxProxyServerInstanceConfig)); //log.debug(`zluxProxyServerInstanceConfig ${JSON.stringify(zluxProxyServerInstanceConfig)}`) @@ -285,17 +276,29 @@ ApimlConnector.prototype = { }); }, - getServiceUrls(defaultUrl) { - const discoveryServiceList = process.env['ZWE_DISCOVERY_SERVICES_LIST'] || ''; - const serviceUrls = discoveryServiceList - .split(',') - .map(url => url.trim()) - .filter(url => url.length > 0) - .map(url => url + (url.endsWith('/') ? '' : '/') + 'apps'); - if (serviceUrls.length === 0) { - serviceUrls.push(defaultUrl); - } - return serviceUrls; + getServiceUrls() { + return this.discoveryUrls.map(url => url + (url.endsWith('/') ? '' : '/') + 'apps'); + }, + + getRequestOptionsArray(method, path) { + return this.discoveryUrls.map((url)=>{ + //in the form of https://host:port/eureka/, trim from https:// and following slash. + const hostAndPort = url.substring(8, url.indexOf('/', 8)).split(':'); + const options = Object.assign({ + host: hostAndPort[0], + port: hostAndPort[1], + method: method, + path: path, + headers: {'accept':'application/json'} + }, this.tlsOptions); + + if (!this.tlsOptions.rejectUnauthorized) { + //Keeping these certs causes an openssl error 46, unknown cert error in a dev environment + delete options.cert; + delete options.key; + } //else, apiml expects a cert and will give a 403. + return options; + }); } /* diff --git a/lib/assets/i18n/log/messages_en.json b/lib/assets/i18n/log/messages_en.json index 46725905..a32d573f 100644 --- a/lib/assets/i18n/log/messages_en.json +++ b/lib/assets/i18n/log/messages_en.json @@ -307,6 +307,7 @@ "ZWED0177W":"Unable to load %s for '%s' into config", "ZWED0178W":"Skipping authentication plugin %s because it's not HA compatible", "ZWED0179W":"Unable to retrieve the list of certificate authorities from the keyring=%s owner=%s Error: %s", + "ZWED0180W":"Failed to query discovery server (%s:%s) for agent access: %s", "ZWED0001E":"RESERVED: Error: %s", "ZWED0002E":"Could not stop language manager for types=%s", diff --git a/lib/index.js b/lib/index.js index 0a058589..4acf4f2c 100755 --- a/lib/index.js +++ b/lib/index.js @@ -219,8 +219,7 @@ Server.prototype = { hostName: webAppOptions.hostname, port: this.port, isHttps: util.isServerHttps(this.zoweConfig), - discoveryHost: apimlConfig.server.hostname, - discoveryPort: apimlConfig.server.port, + discoveryUrls: apimlConfig.server.discoveryUrls || [`https://${apimlConfig.server.discovery}:${apimlConfig.server.port}/eureka/`], tlsOptions: this.tlsOptions, eurekaOverrides: apimlConfig.eureka }); @@ -236,7 +235,7 @@ Server.prototype = { && this.componentConfig.agent.mediationLayer.serviceName && this.componentConfig.node.mediationLayer.server?.gatewayPort) { //at this point, we expect zss to also be attached to the mediation layer, so lets adjust. - webAppOptions.proxiedHost = apimlConfig.server.hostname; + webAppOptions.proxiedHost = apimlConfig.server.gatewayHostname; webAppOptions.proxiedPort = this.componentConfig.node.mediationLayer.server.gatewayPort; if (firstWorker) { yield this.apiml.checkAgent(this.componentConfig.agent.handshakeTimeout, diff --git a/lib/webapp.js b/lib/webapp.js index 96e0b68a..48b5e631 100644 --- a/lib/webapp.js +++ b/lib/webapp.js @@ -268,7 +268,8 @@ function getUserEnv(rbac, zoweConfig){ "ZWED_node_mediationLayer_enabled": nodeConfig.mediationLayer.enabled, "ZWED_node_mediationLayer_server_hostname": nodeConfig.mediationLayer.server.hostname, - + "ZWED_node_mediationLayer_server_gatewayHostname": nodeConfig.mediationLayer.server.gatewayHostname, + //may diverge from above "ZWE_EXTERNAL_HOSTS": process.env.ZWE_EXTERNAL_HOSTS ? process.env.ZWE_EXTERNAL_HOSTS : zoweConfig.zowe.externalDomains.join(','), "ZWE_zowe_externalDomains": zoweConfig.zowe.externalDomains.join(','), diff --git a/plugins/sso-auth/lib/apimlHandler.js b/plugins/sso-auth/lib/apimlHandler.js index 16ebc8d5..17f95d19 100644 --- a/plugins/sso-auth/lib/apimlHandler.js +++ b/plugins/sso-auth/lib/apimlHandler.js @@ -55,7 +55,7 @@ class ApimlHandler { constructor(pluginDef, pluginConf, serverConf, context) { this.logger = context.logger; this.apimlConf = serverConf.node.mediationLayer.server; - this.gatewayUrl = `https://${this.apimlConf.hostname}:${this.apimlConf.gatewayPort}`; + this.gatewayUrl = `https://${this.apimlConf.gatewayHostname}:${this.apimlConf.gatewayPort}`; this.httpsAgent = new https.Agent(context.tlsOptions); } @@ -66,7 +66,7 @@ class ApimlHandler { } const gatewayUrl = this.gatewayUrl; const options = { - hostname: this.apimlConf.hostname, + hostname: this.apimlConf.gatwayHostname, port: this.apimlConf.gatewayPort, //TODO uncertainty about using apicatalog route instead of something part of the gateway itself path: '/apicatalog/api/v1/auth/logout', @@ -194,7 +194,7 @@ class ApimlHandler { } return { - hostname: this.apimlConf.hostname, + hostname: this.apimlConf.gatewayHostname, port: this.apimlConf.gatewayPort, path: path, method: method, diff --git a/plugins/sso-auth/lib/ssoAuth.js b/plugins/sso-auth/lib/ssoAuth.js index f73510fe..be38e022 100644 --- a/plugins/sso-auth/lib/ssoAuth.js +++ b/plugins/sso-auth/lib/ssoAuth.js @@ -18,7 +18,7 @@ const apimlHandlerFactory = require('./apimlHandler'); function doesApimlExist(serverConf) { return ((serverConf.node.mediationLayer !== undefined) && (serverConf.node.mediationLayer.server !== undefined) - && (serverConf.node.mediationLayer.server.hostname !== undefined) + && (serverConf.node.mediationLayer.server.gatewayHostname !== undefined) && (serverConf.node.mediationLayer.server.gatewayPort !== undefined) && (serverConf.node.mediationLayer.server.port !== undefined) && (serverConf.node.mediationLayer.enabled == true)) From 36c26457d2e1735625e2339018acf4c70d5594fa Mon Sep 17 00:00:00 2001 From: 1000TurquoisePogs Date: Tue, 12 Dec 2023 09:25:59 -0500 Subject: [PATCH 2/3] Fixes upon testing behavior in HA Signed-off-by: 1000TurquoisePogs --- lib/apiml.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/apiml.js b/lib/apiml.js index 95c51059..62aa582b 100644 --- a/lib/apiml.js +++ b/lib/apiml.js @@ -110,7 +110,7 @@ ApimlConnector.prototype = { const end = Date.now() + timer; return new Promise((resolve, reject) => { - const optionsArray = getRequestOptionsArray('GET', `/eureka/apps/${serviceName}`); + const optionsArray = this.getRequestOptionsArray('GET', `/eureka/apps/${serviceName}`); let optionsIndex = 0; const issueRequest = () => { @@ -146,7 +146,7 @@ ApimlConnector.prototype = { reject(new Error(`Call timeout when fetching agent status from APIML`)); }); req.on('error', (error) => { - log.warn("ZWES0180W", options.host, options.port, error.message); + log.warn("ZWED0180W", options.host, options.port, error.message); // optionsIndex = (optionsIndex+1) % optionsArray.length; setTimeout(issueRequest, AGENT_CHECK_RECONNECT_DELAY); From 15eca845096523c64655cc858821d77e708ca761 Mon Sep 17 00:00:00 2001 From: 1000TurquoisePogs Date: Tue, 12 Dec 2023 09:29:21 -0500 Subject: [PATCH 3/3] Fix fallback variable for developers Signed-off-by: 1000TurquoisePogs --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 4acf4f2c..072dfe50 100755 --- a/lib/index.js +++ b/lib/index.js @@ -219,7 +219,7 @@ Server.prototype = { hostName: webAppOptions.hostname, port: this.port, isHttps: util.isServerHttps(this.zoweConfig), - discoveryUrls: apimlConfig.server.discoveryUrls || [`https://${apimlConfig.server.discovery}:${apimlConfig.server.port}/eureka/`], + discoveryUrls: apimlConfig.server.discoveryUrls || [`https://${apimlConfig.server.hostname}:${apimlConfig.server.port}/eureka/`], tlsOptions: this.tlsOptions, eurekaOverrides: apimlConfig.eureka });