-
Notifications
You must be signed in to change notification settings - Fork 72
feat: JWT Preflight Check #4531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
hrishikesh-nalawade
wants to merge
3
commits into
v3.x.x
Choose a base branch
from
hrishikesh-nalawade/GH4526/jwt-pre-flight-check
base: v3.x.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,331 @@ | ||
| # Pre-Flight Check Tool | ||
|
|
||
| A Java utility that verifies connectivity to the z/OSMF JWK endpoint **before/after** starting the Zowe API Mediation Layer. This tool helps diagnose configuration issues early such as incorrect hostnames, unreachable ports, missing certificates, or misconfigured z/OSMF by performing a lightweight HTTP(S) call to the z/OSMF JWK endpoint at `/jwt/ibm/api/zOSMFBuilder/jwk`. | ||
|
|
||
| ## Table of Contents | ||
|
|
||
| - [Overview](#overview) | ||
| - [Prerequisites](#prerequisites) | ||
| - [Building](#building) | ||
| - [Usage](#usage) | ||
| - [CLI Flags Reference](#cli-flags-reference) | ||
| - [Certificate Verification Modes](#certificate-verification-modes) | ||
| - [Exit Codes](#exit-codes) | ||
| - [Response Interpretation](#response-interpretation) | ||
| - [Testing Scenarios](#testing-scenarios) | ||
| - [1. Quick Test — DISABLED Mode](#1-quick-test--disabled-mode-no-truststore-needed) | ||
| - [2. STRICT Mode — Full Verification](#2-strict-mode--full-certificate-and-hostname-verification) | ||
| - [3. NONSTRICT Mode — Skip Hostname Check](#3-nonstrict-mode--certificate-chain-verified-hostname-check-skipped) | ||
| - [4. HTTP Mode (No SSL)](#4-http-mode-no-ssl) | ||
| - [5. Validation Error Tests](#5-validation-error-tests) | ||
| - [SAF Keyrings](#saf-keyrings) | ||
| - [Troubleshooting](#troubleshooting) | ||
|
|
||
| --- | ||
|
|
||
| ## Overview | ||
|
|
||
| When Zowe API ML starts, it attempts to reach z/OSMF to obtain public keys for JWT token validation. If z/OSMF is unreachable or misconfigured, the startup fails with errors that can be difficult to diagnose. This pre-flight check tool isolates that connectivity test into a simple, standalone JAR that can be run before Zowe startup. | ||
|
|
||
| **What it checks:** | ||
|
|
||
| - TCP connectivity to the z/OSMF host and port | ||
| - SSL/TLS handshake (when using HTTPS) | ||
| - Certificate trust chain validation (STRICT/NONSTRICT modes) | ||
| - Hostname verification (STRICT mode) | ||
| - HTTP response from the JWK endpoint (`/jwt/ibm/api/zOSMFBuilder/jwk`) | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - **Java 17 or higher** (Java 17, 21, or any later version) | ||
| - Network access to the z/OSMF server | ||
| - A truststore containing the z/OSMF server's CA certificate (required for STRICT and NONSTRICT modes) | ||
|
|
||
| ## Building | ||
|
|
||
| From the root of the `api-layer` repository: | ||
|
|
||
| ```bash | ||
| ./gradlew :pre-flight-check:build | ||
| ``` | ||
|
|
||
| On Windows: | ||
|
|
||
| ```powershell | ||
| .\gradlew :pre-flight-check:build | ||
| ``` | ||
|
|
||
| The fat JAR (with all dependencies bundled) will be generated at: | ||
|
|
||
| ``` | ||
| pre-flight-check/build/libs/pre-flight-check-<version>.jar | ||
| ``` | ||
|
|
||
| For example: `pre-flight-check/build/libs/pre-flight-check-3.5.12-SNAPSHOT.jar` | ||
|
|
||
| ## Usage | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check-<version>.jar --zosmf-host <hostname> --zosmf-port <port> [options] | ||
| ``` | ||
|
|
||
| **Minimal example (DISABLED mode — quickest way to test):** | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check-<version>.jar \ | ||
| --zosmf-host myzosmf.example.com \ | ||
| --zosmf-port 11443 \ | ||
| --verify-certificates DISABLED | ||
| ``` | ||
|
|
||
| **Full example (STRICT mode with truststore):** | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check-<version>.jar \ | ||
| --zosmf-host myzosmf.example.com \ | ||
| --zosmf-port 11443 \ | ||
| --truststore /path/to/truststore.p12 \ | ||
| --truststore-password changeit | ||
| ``` | ||
|
|
||
| **Display help:** | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check-<version>.jar --help | ||
| ``` | ||
|
|
||
| ## CLI Flags Reference | ||
|
|
||
| ### Required Flags | ||
|
|
||
| | Flag | Description | Example | | ||
| |------|-------------|---------| | ||
| | `--zosmf-host` | Hostname or IP address of the z/OSMF server | `--zosmf-host myzosmf.example.com` | | ||
| | `--zosmf-port` | Port number of the z/OSMF server | `--zosmf-port 11443` | | ||
|
|
||
| > **Note:** If `--zosmf-host` or `--zosmf-port` are omitted, picocli will display: | ||
| > `Missing required option: '--zosmf-host=<zosmfHost>'` | ||
|
|
||
| ### Conditionally Required Flags | ||
|
|
||
| These flags are required when `--scheme=https` (the default) and `--verify-certificates` is **not** `DISABLED`: | ||
|
|
||
| | Flag | Description | Error when missing | | ||
| |------|-------------|-------------------| | ||
| | `--truststore` | Path to the truststore file containing the z/OSMF CA certificate | `ERROR: --truststore is required when --scheme=https and verification is not DISABLED.` | | ||
| | `--truststore-password` | Password for the truststore. If specified without a value, you will be prompted interactively. | `ERROR: --truststore-password is required when --scheme=https and verification is not DISABLED.` | | ||
|
|
||
| ### Optional Flags | ||
|
|
||
| | Flag | Default | Description | | ||
| |------|---------|-------------| | ||
| | `--scheme` | `https` | Protocol to use: `http` or `https` | | ||
| | `--verify-certificates` | `STRICT` | Certificate verification mode: `STRICT`, `NONSTRICT`, or `DISABLED` | | ||
| | `--truststore-type` | `PKCS12` | Format of the truststore file (e.g., `PKCS12`, `JKS`, `JCERACFKS`) | | ||
| | `--keystore` | *(none)* | Path to keystore file (only needed for mutual TLS / client certificate authentication) | | ||
| | `--keystore-password` | *(none)* | Password for the keystore. If specified without a value, you will be prompted interactively. | | ||
| | `--keystore-type` | `PKCS12` | Format of the keystore file | | ||
| | `-h`, `--help` | | Display usage help and exit | | ||
|
|
||
| ## Certificate Verification Modes | ||
|
|
||
| The `--verify-certificates` flag controls how SSL/TLS certificates are validated when connecting over HTTPS. This mirrors the `zowe.verifyCertificates` setting in the Zowe configuration (`zowe.yaml`). | ||
|
|
||
| ### STRICT (Default) | ||
|
|
||
| ```bash | ||
| --verify-certificates STRICT | ||
| ``` | ||
|
|
||
| - **Certificate chain**: Fully validated against the truststore | ||
| - **Hostname verification**: The server certificate's CN/SAN must match the `--zosmf-host` value | ||
| - **Truststore**: Required | ||
| - **Use case**: Production environments — maximum security | ||
|
|
||
| ### NONSTRICT | ||
|
|
||
| ```bash | ||
| --verify-certificates NONSTRICT | ||
| ``` | ||
|
|
||
| - **Certificate chain**: Fully validated against the truststore | ||
| - **Hostname verification**: Skipped — the server certificate does not need to match the hostname | ||
| - **Truststore**: Required | ||
| - **Use case**: Environments where the z/OSMF certificate is issued for a different hostname (e.g., accessing via IP address when the cert has a DNS name) | ||
|
|
||
| ### DISABLED | ||
|
|
||
| ```bash | ||
| --verify-certificates DISABLED | ||
| ``` | ||
|
|
||
| - **Certificate chain**: Not validated — all certificates are trusted | ||
| - **Hostname verification**: Skipped | ||
| - **Truststore**: Not required | ||
| - **Use case**: Development/testing environments, or initial connectivity debugging | ||
| - **Warning**: Prints `WARNING: SSL certificate verification is DISABLED. All certificates will be trusted.` | ||
|
|
||
| > **Security Note:** `DISABLED` mode should **never** be used in production. It is vulnerable to man-in-the-middle attacks. | ||
|
|
||
| ## Exit Codes | ||
|
|
||
| | Code | Meaning | | ||
| |------|---------| | ||
| | `0` | **Success** — z/OSMF JWK endpoint is reachable and responding | | ||
| | `4` | **Failure** — connection failed, SSL error, endpoint not found, or configuration error | | ||
| | `8` | **Help** — help/version was displayed; no check was performed | | ||
|
|
||
| ## Response Interpretation | ||
|
|
||
| The tool interprets HTTP response codes from the z/OSMF JWK endpoint as follows: | ||
|
|
||
| | HTTP Code | Result | Message | | ||
| |-----------|--------|---------| | ||
| | 200-299 | **SUCCESS** | `z/OSMF JWK endpoint is reachable and responding. HTTP <code>` | | ||
| | 401 | **SUCCESS** | `z/OSMF JWK endpoint exists (returned 401 Unauthorized — expected without credentials). HTTP 401` | | ||
| | 404 | **FAILURE** | `z/OSMF JWK endpoint not found. HTTP 404` — Consider configuring `jwtAutoConfiguration` to LTPA | | ||
| | 4xx (other) | **FAILURE** | `z/OSMF JWK endpoint returned unexpected client error. HTTP <code>` | | ||
| | 5xx | **FAILURE** | `z/OSMF JWK endpoint returned server error. HTTP <code>` | | ||
|
|
||
| **Note:** A `401 Unauthorized` is treated as **success** because the tool does not send authentication credentials. A 401 confirms the endpoint exists and z/OSMF is processing requests. | ||
|
|
||
| ### Connection-Level Errors | ||
|
|
||
| | Error | Message | | ||
| |-------|---------| | ||
| | SSL handshake failure | `FAILURE: SSL handshake failed. Verify that the truststore contains the z/OSMF server certificate.` | | ||
| | Connection refused | `FAILURE: Cannot connect to <host>:<port>. Verify the host and port are correct and z/OSMF is running.` | | ||
| | Connection timeout | `FAILURE: Connection timed out to <host>:<port>.` | | ||
|
|
||
| ## Testing Scenarios | ||
|
|
||
| Below are step-by-step commands for testing all modes. Replace `<version>` with your actual JAR version (e.g., `3.5.12-SNAPSHOT`) and adjust the host/port for your environment. | ||
|
|
||
| ### 1. Quick Test — DISABLED Mode (No Truststore Needed) | ||
|
|
||
| The fastest way to verify basic TCP + HTTP connectivity: | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check/build/libs/pre-flight-check-<version>.jar \ | ||
| --zosmf-host myzosmf.example.com \ | ||
| --zosmf-port 11443 \ | ||
| --verify-certificates DISABLED | ||
| ``` | ||
|
|
||
| **Expected output (success):** | ||
|
|
||
| ``` | ||
| WARNING: SSL certificate verification is DISABLED. All certificates will be trusted. | ||
| Checking z/OSMF JWK endpoint: https://myzosmf.example.com:11443/jwt/ibm/api/zOSMFBuilder/jwk | ||
| SUCCESS: z/OSMF JWK endpoint exists (returned 401 Unauthorized — expected without credentials). HTTP 401 | ||
| ``` | ||
|
|
||
| ### 2. STRICT Mode — Full Certificate and Hostname Verification | ||
|
|
||
| Requires a truststore containing the z/OSMF server's CA certificate (see [Creating a Truststore](#creating-a-truststore)): | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check/build/libs/pre-flight-check-<version>.jar \ | ||
| --zosmf-host myzosmf.example.com \ | ||
| --zosmf-port 11443 \ | ||
| --truststore /path/to/zosmf-truststore.p12 \ | ||
| --truststore-password password | ||
| ``` | ||
|
|
||
| **Expected output (success):** | ||
|
|
||
| ``` | ||
| Checking z/OSMF JWK endpoint: https://myzosmf.example.com:11443/jwt/ibm/api/zOSMFBuilder/jwk | ||
| SUCCESS: z/OSMF JWK endpoint exists (returned 401 Unauthorized — expected without credentials). HTTP 401 | ||
| ``` | ||
|
|
||
| **Expected output (SSL failure — wrong truststore):** | ||
|
|
||
| ``` | ||
| FAILURE: SSL handshake failed when connecting to https://myzosmf.example.com:11443/jwt/ibm/api/zOSMFBuilder/jwk. | ||
| Verify that the truststore contains the z/OSMF server certificate. | ||
| Details: PKIX path building failed: ...unable to find valid certification path to requested target | ||
| ``` | ||
|
|
||
| ### 3. NONSTRICT Mode — Certificate Chain Verified, Hostname Check Skipped | ||
|
|
||
| Useful when connecting via IP address but the certificate has a DNS name: | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check/build/libs/pre-flight-check-<version>.jar \ | ||
| --zosmf-host 10.0.0.50 \ | ||
| --zosmf-port 11443 \ | ||
| --truststore /path/to/zosmf-truststore.p12 \ | ||
| --truststore-password password \ | ||
| --verify-certificates NONSTRICT | ||
| ``` | ||
|
|
||
| **Expected output (success):** | ||
|
|
||
| ``` | ||
| INFO: Hostname verification is disabled (NONSTRICT mode). | ||
| Checking z/OSMF JWK endpoint: https://10.0.0.50:11443/jwt/ibm/api/zOSMFBuilder/jwk | ||
| SUCCESS: z/OSMF JWK endpoint exists (returned 401 Unauthorized — expected without credentials). HTTP 401 | ||
| ``` | ||
|
|
||
| ### 4. HTTP Mode (No SSL) | ||
|
|
||
| For z/OSMF instances running on plain HTTP (uncommon): | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check/build/libs/pre-flight-check-<version>.jar \ | ||
| --zosmf-host myzosmf.example.com \ | ||
| --zosmf-port 80 \ | ||
| --scheme http | ||
| ``` | ||
|
|
||
| ### 5. Validation Error Tests | ||
|
|
||
| **Missing required flags:** | ||
|
|
||
| ```bash | ||
| # No arguments at all | ||
| java -jar pre-flight-check-<version>.jar | ||
| # Output: Missing required options: '--zosmf-host=<zosmfHost>', '--zosmf-port=<zosmfPort>' | ||
|
|
||
| # Missing truststore in STRICT mode | ||
| java -jar pre-flight-check-<version>.jar --zosmf-host myhost --zosmf-port 443 | ||
| # Output: ERROR: --truststore is required when --scheme=https and verification is not DISABLED. | ||
|
|
||
| # Missing truststore password | ||
| java -jar pre-flight-check-<version>.jar --zosmf-host myhost --zosmf-port 443 --truststore my.p12 | ||
| # Output: ERROR: --truststore-password is required when --scheme=https and verification is not DISABLED. | ||
| ``` | ||
|
|
||
| **Invalid values:** | ||
|
|
||
| ```bash | ||
| # Invalid scheme | ||
| java -jar pre-flight-check-<version>.jar --zosmf-host myhost --zosmf-port 443 --scheme ftp | ||
| # Output: ERROR: --scheme must be 'http' or 'https', got: ftp | ||
|
|
||
| # Invalid verify mode | ||
| java -jar pre-flight-check-<version>.jar --zosmf-host myhost --zosmf-port 443 --verify-certificates INVALID | ||
| # Output: ERROR: --verify-certificates must be STRICT, NONSTRICT, or DISABLED, got: INVALID | ||
| ``` | ||
|
|
||
| **Unreachable host:** | ||
|
|
||
| ```bash | ||
| java -jar pre-flight-check-<version>.jar --zosmf-host nonexistent.host --zosmf-port 443 --verify-certificates DISABLED | ||
| # Output: FAILURE: Cannot connect to nonexistent.host:443. | ||
| ``` | ||
|
|
||
| ## SAF Keyrings | ||
|
|
||
| On z/OS, if you are using SAF keyrings instead of file-based keystores/truststores, provide the keyring path in the `safkeyring://` format and add the JVM protocol handler: | ||
|
|
||
| ```bash | ||
| java -Djava.protocol.handler.pkgs=com.ibm.crypto.provider \ | ||
| -jar pre-flight-check-<version>.jar \ | ||
| --zosmf-host myzosmf.example.com \ | ||
| --zosmf-port 11443 \ | ||
| --truststore safkeyring://IZUSVR/ZoweKeyring \ | ||
| --truststore-password password \ | ||
| --truststore-type JCERACFKS | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| plugins { | ||
| id 'java' | ||
| } | ||
|
|
||
| dependencies { | ||
| implementation libs.picocli | ||
| annotationProcessor libs.picocli.codegen | ||
|
|
||
| testImplementation libs.mockito.core | ||
| testImplementation libs.hamcrest | ||
| } | ||
|
|
||
| compileJava { | ||
| options.compilerArgs += ["-Aproject=${project.group}/${project.name}"] | ||
| } | ||
|
|
||
| jar { | ||
| manifest { | ||
| attributes( | ||
| 'Main-Class': 'org.zowe.apiml.PreFlightCheck' | ||
| ) | ||
| } | ||
| duplicatesStrategy = DuplicatesStrategy.EXCLUDE | ||
| from { | ||
| configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we rename this? There are already some pre-flight checks and some in progress. This name is too general.
For example:
zosmf-jwt-checkjwt-check