diff --git a/.github/workflows/build-packaging.yml b/.github/workflows/build-packaging.yml index 84cce4c16c..aa043001a8 100644 --- a/.github/workflows/build-packaging.yml +++ b/.github/workflows/build-packaging.yml @@ -276,7 +276,7 @@ jobs: with: manifest-file-path: ${{ github.workspace }}/manifest.json default-target-path: .pax/binaryDependencies/ - expected-count: 33 + expected-count: 34 # this step is not doing a publish, we are just utilizing this actions to get the PUBLISH_TARGET_PATH, # and it will be used in the next step: [Download 3] Download SMPE build log diff --git a/.pax/pre-packaging.sh b/.pax/pre-packaging.sh index fd557f61b9..42a43c79a1 100755 --- a/.pax/pre-packaging.sh +++ b/.pax/pre-packaging.sh @@ -320,6 +320,11 @@ echo "[$SCRIPT_NAME] move certificate_analyser $certificate_analyser" mkdir -p "${ZOWE_ROOT_DIR}/bin/utils" mv "${certificate_analyser}" "${ZOWE_ROOT_DIR}/bin/utils/certificate-analyser.jar" +zosmf_jwt_check=$(find "${ZOWE_ROOT_DIR}/files" -type f \( -name "zosmf-jwt-check*.jar" \) | head -n 1) +echo "[$SCRIPT_NAME] move zosmf_jwt_check $zosmf_jwt_check" +mkdir -p "${ZOWE_ROOT_DIR}/bin/utils" +mv "${zosmf_jwt_check}" "${ZOWE_ROOT_DIR}/bin/utils/zosmf-jwt-check.jar" + echo "[$SCRIPT_NAME] create dummy zowe.yaml for install" cat <>"${BASE_DIR}/zowe.yaml" diff --git a/.pax/prepare-workspace.sh b/.pax/prepare-workspace.sh index 4dfba7af18..0dd6a2aea5 100755 --- a/.pax/prepare-workspace.sh +++ b/.pax/prepare-workspace.sh @@ -191,6 +191,7 @@ mv *.pax.Z "${CONTENT_DIR}/files/" mv *.pax "${CONTENT_DIR}/files/" mv *.zip "${CONTENT_DIR}/files/" mv certificate-analyser-* "${CONTENT_DIR}/files/" +mv zosmf-jwt-check-* "${CONTENT_DIR}/files/" # PAX_BINARY_DEPENDENCIES should be empty now if [ -n "$(ls -1)" ]; then echo "[$SCRIPT_NAME] Error: binaryDependencies directory is not clean" diff --git a/CHANGELOG.md b/CHANGELOG.md index 0383c45972..0d8271b318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to the Zowe Installer will be documented in this file. ## `3.5.0` - +- Enhancement: Added command `zwe validate zosmf jwt` which checks whether a Zowe configured to use z/OSMF is able to use z/OSMF's JWT authentication method or not. This command runs every startup if the conditions are met, but can be disabled or turned into only a warning via YAML property `zowe.launchScript.startupChecks.zosmfjwt` being set to "warn" or "disabled". [#4727](https://github.com/zowe/zowe-install-packaging/pull/4727) - Enhancement: Split `zwe components install` into subcommands with dry-run options to help you with each step of component installation. [#4582](https://github.com/zowe/zowe-install-packaging/pull/4582) - Enhancement: Added command `zwe vadlidate certificate` which checks Zowe's keystore and truststore and reports action items to resolve any issues found. This is run automatically every Zowe setup but that can be changed via the parameter `zowe.launchScript.startupChecks.certificate`, which can be changed to "warn" to warn only, or "disabled" to bypass the check. [#4554](https://github.com/zowe/zowe-install-packaging/pull/4554) - Enhancement: `zwe version` supports displaying Zowe version defined in the configuration. [#4322](https://github.com/zowe/zowe-install-packaging/pull/4322) diff --git a/bin/commands/.errors b/bin/commands/.errors index 7b57342f7f..3539c30ecc 100644 --- a/bin/commands/.errors +++ b/bin/commands/.errors @@ -56,3 +56,8 @@ ZWEL0322E|322|%s is not a valid directory. ZWEL0323E|323|Certificate validation failed. Fix errors listed before starting Zowe. ZWEL0326E|326|An error occurred while processing Zowe YAML config %s: ZWEL0327E|327|Failed to read %s - %s. +ZWEL0359E|359|z/OSMF host (zosmf.host) or port (zosmf.port) is not configured in the Zowe YAML. Cannot validate z/OSMF JWT support without a z/OSMF destination. +ZWEL0360E|360|Neither components.discovery.enabled nor components.apiml.enabled is set to true. The required APIML servers are not enabled for z/OSMF access. +ZWEL0361W||z/OSMF JWT check failed (rc=%s) but jwtAutoconfiguration is 'ltpa', so JWT support is not required for this Zowe configuration. +ZWEL0362E|362|z/OSMF JWT check failed (rc=%s). Zowe requires z/OSMF JWT support (jwtAutoconfiguration is 'jwt') but it is not working. + diff --git a/bin/commands/internal/start/prepare/index.ts b/bin/commands/internal/start/prepare/index.ts index 8f883f42ce..c31a9cb234 100644 --- a/bin/commands/internal/start/prepare/index.ts +++ b/bin/commands/internal/start/prepare/index.ts @@ -29,6 +29,7 @@ import * as node from '../../../../libs/node'; import * as zosmf from '../../../../libs/zosmf'; import * as zoslib from '../../../../libs/zos'; import * as validateBind from '../../../validate/port/bind/index'; +import * as validateZosmfJwt from '../../../validate/zosmf/jwt/index'; import * as validateCertificate from '../../../validate/certificate/index'; //# This command prepares everything needed to start Zowe. @@ -217,6 +218,14 @@ function globalValidate(enabledComponents:string[]): void { privateErrors++; common.printFormattedError('ZWELS', "zwe-internal-start-prepare,global_validate", "Zosmf validation failed"); } + const validateZosmfJwtAction = getStartupCheckMode('zosmfjwt'); + if (validateZosmfJwtAction.doCheck) { + const jwtRc = validateZosmfJwt.execute(!validateZosmfJwtAction.warnOnly); + if (jwtRc !== 0) { + privateErrors++; + common.printFormattedError('ZWELS', "zwe-internal-start-prepare,global_validate", "Zosmf JWT validation failed"); + } + } } else if (enabledComponents.includes('gateway') && std.getenv('ZWE_components_gateway_apiml_security_auth_provider') == "zosmf") { privateErrors++; common.printError("Using z/OSMF as 'components.gateway.apiml.security.auth.provider' is not possible: discovery is disabled."); diff --git a/bin/commands/validate/zosmf/jwt/.errors b/bin/commands/validate/zosmf/jwt/.errors new file mode 100644 index 0000000000..407c4a37a7 --- /dev/null +++ b/bin/commands/validate/zosmf/jwt/.errors @@ -0,0 +1,4 @@ +ZWEL0359E|359|z/OSMF host (zosmf.host) or port (zosmf.port) is not configured in the Zowe YAML. Cannot validate z/OSMF JWT support without a z/OSMF destination. +ZWEL0360E|360|Neither components.discovery.enabled nor components.apiml.enabled is set to true. The required APIML servers are not enabled for z/OSMF access. +ZWEL0361W||z/OSMF JWT check failed (rc=%s) but jwtAutoconfiguration is 'ltpa', so JWT support is not required for this Zowe configuration. +ZWEL0362E|362|z/OSMF JWT check failed (rc=%s). Zowe requires z/OSMF JWT support (jwtAutoconfiguration is 'jwt') but it is not working. diff --git a/bin/commands/validate/zosmf/jwt/.examples b/bin/commands/validate/zosmf/jwt/.examples new file mode 100644 index 0000000000..c94c7d41ca --- /dev/null +++ b/bin/commands/validate/zosmf/jwt/.examples @@ -0,0 +1,3 @@ +zwe validate zosmf jwt -c /path/to/zowe.yaml +zwe validate zosmf jwt -c 'FILE(/customizations/zowe.yaml):FILE(/defaults/zowe.yaml)' +zwe validate zosmf jwt -c 'FILE(/path/to/zowe.yaml):PARMLIB(ZOWE.PARMLIB(YAML))' diff --git a/bin/commands/validate/zosmf/jwt/.help b/bin/commands/validate/zosmf/jwt/.help new file mode 100644 index 0000000000..8f8445b19b --- /dev/null +++ b/bin/commands/validate/zosmf/jwt/.help @@ -0,0 +1,17 @@ +Checks whether z/OSMF JWT support is correctly configured and operational when Zowe is set +to use z/OSMF as the authentication provider with JWT autoconfiguration. + +This command reads the Zowe YAML configuration to determine whether the check is applicable, +then invokes the zosmf-jwt-check.jar utility to probe z/OSMF for JWT support. + +If Zowe is not configured to use z/OSMF as the authentication provider, the check succeeds +immediately with an informational message. + +If jwtAutoconfiguration is 'ltpa' and the check fails, a warning is issued but the command +still exits successfully, because LTPA does not require z/OSMF JWT support. + +If jwtAutoconfiguration is 'jwt' and the check fails, the command exits with an error and +provides detailed diagnostic information. + +The behavior of this check during Zowe startup can be controlled via: + zowe.launchScript.startupChecks.zosmfjwt: exit | warn | disabled diff --git a/bin/commands/validate/zosmf/jwt/cli.ts b/bin/commands/validate/zosmf/jwt/cli.ts new file mode 100644 index 0000000000..c9e42e95a7 --- /dev/null +++ b/bin/commands/validate/zosmf/jwt/cli.ts @@ -0,0 +1,16 @@ +/* + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v2.0 which + accompanies this distribution, and is available at + https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ +import * as index from './index'; +import * as configmgr from '../../../../libs/configmgr'; + +index.execute(false); + +configmgr.cleanupTempDir(); diff --git a/bin/commands/validate/zosmf/jwt/index.sh b/bin/commands/validate/zosmf/jwt/index.sh new file mode 100644 index 0000000000..49ffa01fd7 --- /dev/null +++ b/bin/commands/validate/zosmf/jwt/index.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +####################################################################### +# This program and the accompanying materials are made available +# under the terms of the Eclipse Public License v2.0 which +# accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-v20.html +# +# SPDX-License-Identifier: EPL-2.0 +# +# Copyright Contributors to the Zowe Project. +####################################################################### + +if [ -z "${ZWE_PRIVATE_TMP_MERGED_YAML_DIR}" ]; then + # user-facing command, use tmpdir to not mess up workspace permissions + export ZWE_PRIVATE_TMP_MERGED_YAML_DIR=1 +fi +_CEE_RUNOPTS="XPLINK(ON),HEAPPOOLS(OFF),HEAPPOOLS64(OFF)" ${ZWE_zowe_runtimeDirectory}/bin/utils/configmgr -script "${ZWE_zowe_runtimeDirectory}/bin/commands/validate/zosmf/jwt/cli.js" diff --git a/bin/commands/validate/zosmf/jwt/index.ts b/bin/commands/validate/zosmf/jwt/index.ts new file mode 100644 index 0000000000..3385aa310c --- /dev/null +++ b/bin/commands/validate/zosmf/jwt/index.ts @@ -0,0 +1,159 @@ +/* + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v2.0 which + accompanies this distribution, and is available at + https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +import * as std from 'cm_std'; +import * as common from '../../../../libs/common'; +import * as config from '../../../../libs/config'; +import * as java from '../../../../libs/java'; +import * as shell from '../../../../libs/shell'; + +const COMMAND_NAME = 'zwe-validate-zosmf-jwt'; + +export function execute(quitOnError?: boolean): number { + common.requireZoweYaml(); + const ZOWE_CONFIG = config.getZoweConfig(); + + // Verify that zosmf.host and zosmf.port are configured. + const zosmfHost = ZOWE_CONFIG.zOSMF?.host; + const zosmfPort = ZOWE_CONFIG.zOSMF?.port; + if (!zosmfHost || !zosmfPort) { + const msg = `ZWEL0359E: z/OSMF host (zosmf.host) or port (zosmf.port) is not configured in the Zowe YAML.` + + ` Cannot validate z/OSMF JWT support without a z/OSMF destination.`; + if (quitOnError) { + common.printErrorAndExit(msg, undefined, 359); + } else { + common.printFormattedError(common.MSG_KEY, COMMAND_NAME, msg); + } + return 359; + } + + // Verify that at least one of the required APIML servers is enabled. + const discoveryEnabled = ZOWE_CONFIG.components?.discovery?.enabled === true; + const apimlEnabled = ZOWE_CONFIG.components?.apiml?.enabled === true; + if (!discoveryEnabled && !apimlEnabled) { + const msg = `ZWEL0360E: Neither components.discovery.enabled nor components.apiml.enabled is set to true.` + + ` The required APIML servers are not enabled for z/OSMF access.`; + if (quitOnError) { + common.printErrorAndExit(msg, undefined, 360); + } else { + common.printFormattedError(common.MSG_KEY, COMMAND_NAME, msg); + } + return 360; + } + + // Check whether the gateway is configured to use z/OSMF as the authentication provider. + const authProvider = ZOWE_CONFIG.components?.gateway?.apiml?.security?.auth?.provider; + if (!authProvider || authProvider.toLowerCase() !== 'zosmf') { + common.printFormattedInfo( + common.MSG_KEY, + COMMAND_NAME, + `Zowe is not configured to use z/OSMF as the authentication provider` + + ` (components.gateway.apiml.security.auth.provider=${authProvider || ''}).` + + ` No z/OSMF JWT check is required.` + ); + return 0; + } + + // Determine the jwtAutoconfiguration value to understand what mode we are in. + const jwtAutoconfigRaw = ZOWE_CONFIG.components?.gateway?.apiml?.security?.auth?.zosmf?.jwtAutoconfiguration || ''; + const jwtAutoconfig = jwtAutoconfigRaw.toLowerCase(); + + // Ensure java is available before invoking the jar. + java.requireJava(); + const javaHome = std.getenv('JAVA_HOME'); + const javaExec = `${javaHome}/bin/java`; + + const runtimeDir = std.getenv('ZWE_zowe_runtimeDirectory'); + const jarPath = `${runtimeDir}/bin/utils/zosmf-jwt-check.jar`; + + // Collect truststore parameters from the Zowe YAML. + const verifyCertificates = ZOWE_CONFIG.zowe?.verifyCertificates || 'STRICT'; + const truststoreType = ZOWE_CONFIG.zowe?.certificate?.truststore?.type || ''; + const truststoreFile = ZOWE_CONFIG.zowe?.certificate?.truststore?.file || ''; + const truststorePassword = ZOWE_CONFIG.zowe?.certificate?.truststore?.password || ''; + + common.printFormattedInfo( + common.MSG_KEY, + COMMAND_NAME, + `Running z/OSMF JWT check for ${zosmfHost}:${zosmfPort} (jwtAutoconfiguration=${jwtAutoconfigRaw || ''})` + ); + + const result = shell.execOutErrSync( + javaExec, + '-Djava.protocol.handler.pkgs=com.ibm.crypto.provider', + '-jar', jarPath, + '--zosmf-host', zosmfHost, + '--zosmf-port', String(zosmfPort), + '--verify-certificates', verifyCertificates, + '--truststore-type', truststoreType, + '--truststore-file', truststoreFile, + '--truststore-password', truststorePassword + ); + + if (result.rc !== 0) { + if (jwtAutoconfig === 'ltpa') { + // LTPA mode does not require z/OSMF JWT support - treat as a warning only. + common.printFormattedWarn( + common.MSG_KEY, + COMMAND_NAME, + `ZWEL0361W: z/OSMF JWT check failed (rc=${result.rc}) but jwtAutoconfiguration is 'ltpa',` + + ` so JWT support is not required for this Zowe configuration.` + + ` z/OSMF JWT support may not be available.` + ); + if (result.out) { + common.printDebug(result.out); + } + if (result.err) { + common.printDebug(result.err); + } + return 0; + } + + // JWT mode requires z/OSMF JWT support - this is an error. + const jarOutput = [result.out, result.err].filter(Boolean).join('\n'); + const errorMsg = + `ZWEL0362E: z/OSMF JWT check failed (rc=${result.rc}).` + + ` Zowe requires z/OSMF JWT support (jwtAutoconfiguration is '${jwtAutoconfigRaw}') but it is not working.\n` + + `\nJar output:\n${jarOutput}\n` + + `\nZowe YAML parameters that led to this result:` + + `\n zosmf.host=${zosmfHost}` + + `\n zosmf.port=${zosmfPort}` + + `\n zowe.verifyCertificates=${verifyCertificates}` + + `\n zowe.certificate.truststore.type=${truststoreType}` + + `\n zowe.certificate.truststore.file=${truststoreFile}` + + `\n zowe.certificate.truststore.password=` + + `\n components.gateway.apiml.security.auth.provider=${authProvider}` + + `\n components.gateway.apiml.security.auth.zosmf.jwtAutoconfiguration=${jwtAutoconfigRaw || ''}` + + `\n\nTo resolve this issue:` + + `\n Verify that z/OSMF has been configured for JWT support by reviewing:` + + `\n https://www.ibm.com/docs/en/zos/3.2.0?topic=configurations-enabling-json-web-token-support` + + `\n Also check the z/OSMF joblog for further diagnostic information.` + + `\n\nIf you do not wish to use z/OSMF JWT support, you can instead change:` + + `\n components.gateway.apiml.security.auth.zosmf.jwtAutoconfiguration to 'ltpa'` + + `\n\nIf you believe this validate command is incorrect, you can set it to a warning or disable it:` + + `\n zowe.launchScript.startupChecks.zosmfjwt: warn (report as warning, continue startup)` + + `\n zowe.launchScript.startupChecks.zosmfjwt: disabled (skip this check entirely)`; + + if (quitOnError) { + common.printErrorAndExit(errorMsg, undefined, 362); + } else { + common.printFormattedError(common.MSG_KEY, COMMAND_NAME, errorMsg); + } + return result.rc; + } + + common.printFormattedInfo( + common.MSG_KEY, + COMMAND_NAME, + `z/OSMF JWT check passed. z/OSMF at ${zosmfHost}:${zosmfPort} supports JWT.` + ); + return 0; +} diff --git a/files/defaults.yaml b/files/defaults.yaml index 9087c990e6..0cfe5733e5 100644 --- a/files/defaults.yaml +++ b/files/defaults.yaml @@ -141,6 +141,8 @@ zowe: # zosmf: exit # # Superuser startup check - defaults to zowe.launchScript.startupChecks.default, or "exit" if this is unset # user: exit + # z/OSMF JWT support check - checks whether z/OSMF JWT support is usable when Zowe is configured to use it + # zosmfjwt: exit # # Certificate (zowe.certificate) validation check - defaults to zowe.launchScript.startupChecks.default, or "exit" if this is unset # certificate: exit diff --git a/manifest.json.template b/manifest.json.template index cf41dfe9ae..804c5099e3 100644 --- a/manifest.json.template +++ b/manifest.json.template @@ -61,34 +61,28 @@ "artifact": "*.pax" }, "org.zowe.apiml.apiml-package": { - "version": "^3.0.3-SNAPSHOT", - "artifact": "apiml-*.zip", - "exclusions": ["*PR*.zip"] + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "apiml-*.zip" }, "org.zowe.apiml.api-catalog-package": { - "version": "^3.0.3-SNAPSHOT", - "artifact": "api-catalog-*.zip", - "exclusions": ["*PR*.zip"] + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "api-catalog-*.zip" }, "org.zowe.apiml.discovery-package": { - "version": "^3.0.3-SNAPSHOT", - "artifact": "discovery-*.zip", - "exclusions": ["*PR*.zip"] + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "discovery-*.zip" }, "org.zowe.apiml.gateway-package": { - "version": "^3.0.3-SNAPSHOT", - "artifact": "gateway-*.zip", - "exclusions": ["*PR*.zip"] + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "gateway-*.zip" }, "org.zowe.apiml.caching-service-package": { - "version": "^3.0.3-SNAPSHOT", - "artifact": "caching-service-*.zip", - "exclusions": ["*PR*.zip"] + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "caching-service-*.zip" }, "org.zowe.apiml.apiml-common-lib-package": { - "version": "^3.0.3-SNAPSHOT", - "artifact": "apiml-common-lib-*.zip", - "exclusions": ["*PR*.zip"] + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "apiml-common-lib-*.zip" }, "org.zowe.apiml.sdk.common-java-lib-package": { "version": "^2.0.0-SNAPSHOT", @@ -96,18 +90,20 @@ "exclusions": ["*PR*.zip"] }, "org.zowe.apiml.sdk.apiml-sample-extension-package": { - "version": "^3.0.3-SNAPSHOT", + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", "artifact": "apiml-sample-extension-*.zip" }, "org.zowe.apiml.zaas-package": { - "version": "^3.0.3-SNAPSHOT", - "artifact": "zaas-package-*.zip", - "exclusions": ["*PR*.zip"] + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "zaas-package-*.zip" }, "org.zowe.apiml.sdk.certificate-analyser": { - "version": "^3.0.3-SNAPSHOT", - "artifact": "certificate-analyser-*.jar", - "exclusions": ["*PR*.jar"] + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "certificate-analyser-*.jar" + }, + "org.zowe.apiml.sdk.zosmf-jwt-check": { + "version": "^3.5.13-PR-fixed-Preflight-SNAPSHOT", + "artifact": "zosmf-jwt-check-*.jar" }, "org.zowe.getesm": { "version": "^3.0.0-V3.X-STAGING", diff --git a/schemas/zowe-yaml-schema.json b/schemas/zowe-yaml-schema.json index 7a30a74fab..4ca0f69c14 100644 --- a/schemas/zowe-yaml-schema.json +++ b/schemas/zowe-yaml-schema.json @@ -741,6 +741,11 @@ "$ref": "/schemas/v2/server-common#startupCheck", "default": "exit", "description": "Checks if zowe.setup.security.users.zowe (ZWESVUSR) is configured as a superuser (UID 0). Such a setting is strongly discouraged." + }, + "zosmfjwt": { + "$ref": "/schemas/v2/server-common#startupCheck", + "default": "exit", + "description": "Checks whether z/OSMF JWT support is correctly configured when Zowe is set to use z/OSMF as the authentication provider with JWT autoconfiguration. Can be set to 'exit', 'warn', or 'disabled'." } } }