diff --git a/CHANGELOG.md b/CHANGELOG.md index 165a794..e77c21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,8 +45,9 @@ Consequences: ### ⚠️ Breaking — the CXF backend was removed Version 2.0.0 removes the legacy Apache CXF backend. The dependency-free client introduced in the -previous release is the only implementation; the public API is unchanged, so calling code is -unaffected. Consequences: +previous release is the only implementation; the documented entry points are unchanged, so typical +calling code is unaffected (though a few CXF/SMB-only public types were removed — see Removed). +Consequences: - **WinRM over HTTPS with self-signed certificates**: unlike the CXF-based client — which silently trusted every TLS certificate and skipped hostname verification — this client **validates the @@ -56,8 +57,6 @@ unaffected. Consequences: (`-Djavax.net.ssl.trustStore=...`); or - disable TLS validation with `-Dorg.metricshub.winrm.tls.insecure=true` (**insecure — for testing only**). -- Setting `-Dorg.metricshub.winrm.backend=cxf` now fails with a clear error instead of selecting - the removed backend: remove the property (or stay on winrm-java 1.x). - The jar shrinks dramatically: the Apache CXF / JAX-WS / JAXB stack is gone, and with the SMB file copy replaced by a WinRM-native transfer (see above), the library has **zero runtime dependencies**. diff --git a/README.md b/README.md index d5bbec0..1823662 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,6 @@ ![GitHub top language](https://img.shields.io/github/languages/top/metricshub/winrm-java) ![License](https://img.shields.io/github/license/metricshub/winrm-java) -This project uses [WS-Man Client](https://github.com/OpenNMS/wsman) and [winrm4j](https://github.com/cloudsoft/winrm4j/) - See **[Project Documentation](https://metricshub.org/winrm-java)** and the [Javadoc](https://metricshub.org/winrm-java/apidocs) for more information on how to use this library in your code. The Windows Remote Management (WinRM) Java Client is a library that enables to: @@ -16,7 +14,8 @@ The Windows Remote Management (WinRM) Java Client is a library that enables to: > ## ⚠️ Upgrading from 1.x > > Version 2.0.0 **removed the legacy Apache CXF backend**: the dependency-free **light** client is -> the only implementation (same public API — calling code is unaffected). Two consequences: +> the only implementation (same documented entry points; a few CXF/SMB-only public types were +> removed). The main consequence: > > * Unlike the CXF-based client, which silently trusted every TLS certificate, the light client > **validates the server certificate and verifies the hostname by default**. @@ -24,8 +23,6 @@ The Windows Remote Management (WinRM) Java Client is a library that enables to: > will fail** during the TLS handshake unless you install the server certificate (or its issuing > CA) into a Java trust store (e.g. `-Djavax.net.ssl.trustStore=...`) or disable TLS validation > with `-Dorg.metricshub.winrm.tls.insecure=true` (**insecure — for testing only**). -> * Setting `-Dorg.metricshub.winrm.backend=cxf` now fails with a clear error instead of selecting -> the removed backend. Remove the property (or stay on winrm-java 1.x). ## The WinRM client diff --git a/pom.xml b/pom.xml index 00c8572..5d5105e 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,8 @@ - 2025-04-03T12:15:29Z + + 2026-07-24T00:00:00Z @@ -202,12 +203,82 @@ + + + maven-site-plugin + 4.0.0-M16 + + + org.sentrysoftware.maven + maven-skin-tools + 1.8.00 + + + + + + + + + maven-project-info-reports-plugin + 3.9.0 + + + + ci-management + dependencies + dependency-info + distribution-management + issue-management + licenses + plugins + scm + summary + team + + + + + + + + org.apache.maven.plugins + maven-jxr-plugin + 3.6.0 + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + javadoc + + + + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 3.5.6 + + maven-pmd-plugin @@ -222,6 +293,17 @@ + + + maven-changelog-plugin + + + + + + + diff --git a/src/site/markdown/authentication.md b/src/site/markdown/authentication.md new file mode 100644 index 0000000..4cd6ecb --- /dev/null +++ b/src/site/markdown/authentication.md @@ -0,0 +1,111 @@ +keywords: authentication, ntlm, kerberos, spnego, domain, realm, kdc, krb5, ticket cache +description: Authenticate to WinRM with NTLM or Kerberos (SPNEGO), including domain accounts and Kerberos configuration. + +# Authentication + + + +The client authenticates with either **NTLM** or **Kerberos (SPNEGO)**. The scheme is chosen by the +`authentications` argument, a +`List<`[`AuthenticationEnum`](apidocs/org/metricshub/winrm/service/client/auth/AuthenticationEnum.html)`>` +that both `executeWql(...)` and `WinRMCommandExecutor.execute(...)` accept. + +```java +import static org.metricshub.winrm.service.client.auth.AuthenticationEnum.KERBEROS; +import static org.metricshub.winrm.service.client.auth.AuthenticationEnum.NTLM; + +singletonList(NTLM); // NTLM only (also the default when null or empty) +singletonList(KERBEROS); // Kerberos only +``` + +If the list is `null` or empty, **NTLM** is used. + +## User name and domain + +The user name may be given as `DOMAIN\user` or as a bare `user`. When a backslash is present, the +part before it is treated as the Windows domain and the part after it as the account name. In Java, +remember to escape the backslash in a string literal: + +```java +"DOMAIN\\Administrator" // domain = DOMAIN, user = Administrator +"Administrator" // no domain +``` + +## NTLM + +NTLM is the default. It works over both transports: + +* **HTTP** — the WinRM payload is protected with **NTLM message encryption**, so credentials and + data are not sent in the clear even without TLS. +* **HTTPS** — NTLM runs inside the TLS tunnel. See [TLS / HTTPS](tls.html). + +NTLM needs no extra configuration beyond the user name and password. + +## Kerberos (SPNEGO) + +Kerberos authentication uses SPNEGO through the JDK's GSS-API and **requires HTTPS**. + +```java +import static org.metricshub.winrm.WinRMHttpProtocolEnum.HTTPS; +import static org.metricshub.winrm.service.client.auth.AuthenticationEnum.KERBEROS; + +executeWql( + HTTPS, "server.example.com", null, + "DOMAIN\\Administrator", password, null, + "SELECT Name FROM Win32_ComputerSystem", + 30_000L, + ticketCache, // optional java.nio.file.Path to a ticket cache + singletonList(KERBEROS) +); +``` + +### Kerberos configuration + +By default, Kerberos relies on the **ambient JDK Kerberos configuration** — the platform `krb5.conf` +(or the file named by `-Djava.security.krb5.conf`), or the realm and KDC given directly with +`-Djava.security.krb5.realm` and `-Djava.security.krb5.kdc`: + +```bash +java -Djava.security.krb5.realm=EXAMPLE.COM \ + -Djava.security.krb5.kdc=dc01.example.com \ + -cp ... MyApp +``` + +The optional `ticketCache` parameter points to a Kerberos ticket cache to use for the connection. + +## Choosing the scheme on the command line + +The standalone jar selects the scheme with `--ntlm` (the default) or `--kerberos`. The two are +mutually exclusive, and `--kerberos` requires `--https`: + +```bash +java -jar ${project.artifactId}-${project.version}-standalone.jar \ + -h server.example.com -u 'DOMAIN\user' -pf password.txt \ + --https --kerberos \ + command whoami +``` + +Instead of relying on the ambient configuration, the CLI can set the JDK Kerberos configuration for +the current invocation: + +| Option | Meaning | +| --- | --- | +| `--kerberos-kdc ` | Sets the KDC and, unless `--kerberos-realm` is given, infers the realm from the KDC's DNS suffix. | +| `--kerberos-realm ` | Overrides the inferred realm. Requires `--kerberos-kdc`. | + +```bash +java -jar ${project.artifactId}-${project.version}-standalone.jar \ + -h server.internal.example.com -u 'DOMAIN\user' -pf password.txt \ + --https --kerberos --kerberos-kdc dc01.internal.example.com \ + command whoami +``` + +Here the realm is inferred as `INTERNAL.EXAMPLE.COM` by dropping the KDC's first DNS label and +upper-casing the rest. This follows a common Active Directory naming convention but is not +guaranteed by Kerberos — pass `--kerberos-realm` when the realm does not match the KDC's DNS suffix, +or when the KDC is not a fully qualified DNS name. + +## See also + +* [TLS / HTTPS](tls.html) — required for Kerberos and recommended for NTLM +* [Timeouts and Errors](timeouts-and-errors.html) — how authentication failures surface diff --git a/src/site/markdown/commands.md b/src/site/markdown/commands.md new file mode 100644 index 0000000..f14414f --- /dev/null +++ b/src/site/markdown/commands.md @@ -0,0 +1,142 @@ +keywords: command, execute, cmd, stdout, stderr, exit code, file copy, script +description: Execute remote commands with WinRMCommandExecutor, capture output and exit codes, and copy local files to the host. + +# Remote Commands + + + +The client can run an arbitrary command on the remote host and hand back its standard output, +standard error, and exit code. It can also copy local script files to the host first and rewrite the +command so it references them. + +## `WinRMCommandExecutor.execute(...)` + +Commands are run with the static method +[`WinRMCommandExecutor.execute(...)`](apidocs/org/metricshub/winrm/command/WinRMCommandExecutor.html), +which returns a +[`WindowsRemoteCommandResult`](apidocs/org/metricshub/winrm/WindowsRemoteCommandResult.html). + +```java +import static java.util.Collections.singletonList; +import static org.metricshub.winrm.WinRMHttpProtocolEnum.HTTPS; +import static org.metricshub.winrm.service.client.auth.AuthenticationEnum.NTLM; + +import org.metricshub.winrm.WindowsRemoteCommandResult; +import org.metricshub.winrm.command.WinRMCommandExecutor; + +WindowsRemoteCommandResult result = WinRMCommandExecutor.execute( + "ipconfig /all", // command (mandatory) + HTTPS, // protocol + "server.example.com", // hostname (mandatory) + null, // port (null → 5985 for HTTP, 5986 for HTTPS) + "DOMAIN\\Administrator", // username (mandatory) + "the-password".toCharArray(), // password + null, // working directory (nullable) + 30_000L, // timeout in milliseconds + null, // local files to copy (nullable) + null, // Kerberos ticket cache (null for NTLM) + singletonList(NTLM) // authentication schemes +); + +System.out.println("exit code: " + result.getStatusCode()); +System.out.print(result.getStdout()); +System.err.print(result.getStderr()); +``` + +### Parameters + +| Parameter | Type | Notes | +| --- | --- | --- | +| `command` | `String` | The command line to run. **Mandatory.** | +| `protocol` | [`WinRMHttpProtocolEnum`](apidocs/org/metricshub/winrm/WinRMHttpProtocolEnum.html) | `HTTP` or `HTTPS`. `null` defaults to `HTTP`. | +| `hostname` | `String` | Host name or IP address. **Mandatory.** | +| `port` | `Integer` | `null` uses the protocol default. | +| `username` | `String` | `DOMAIN\user` or `user`. **Mandatory.** | +| `password` | `char[]` | The password. | +| `workingDirectory` | `String` | Working directory of the spawned process on the remote host. May be `null`. | +| `timeout` | `long` | Timeout in milliseconds. Must be **greater than zero**. | +| `localFileToCopyList` | `List` | Local files to copy to the host before running (see below). May be `null`. | +| `ticketCache` | `java.nio.file.Path` | Kerberos ticket cache path. `null` for NTLM. | +| `authentications` | `List<`[`AuthenticationEnum`](apidocs/org/metricshub/winrm/service/client/auth/AuthenticationEnum.html)`>` | Requested schemes. `null` or empty means NTLM only. | + +## The result + +[`WindowsRemoteCommandResult`](apidocs/org/metricshub/winrm/WindowsRemoteCommandResult.html) is an +immutable value: + +| Method | Returns | Description | +| --- | --- | --- | +| `getStdout()` | `String` | The command's standard output. | +| `getStderr()` | `String` | The command's standard error. | +| `getStatusCode()` | `int` | The process exit code. | +| `getExecutionTime()` | `float` | The measured execution time of the command. | + +## Character set + +The output character set does not need to be specified: the client detects the remote host's active +code page before the command runs and decodes standard output and standard error accordingly. + +## Copying local files to the host + +Pass one or more local paths in `localFileToCopyList` to have them copied to the remote host before +the command runs. Every reference to a listed file in the `command` string is rewritten to the path +where the file lands on the host — typically under `C:\Windows\Temp`. For example: + +```java +WinRMCommandExecutor.execute( + "CSCRIPT c:\\MyScript.vbs", + /* protocol */ HTTPS, + /* hostname */ "server.example.com", + /* port */ null, + /* username */ "DOMAIN\\Administrator", + /* password */ password, + /* workingDir */ null, + /* timeout */ 30_000L, + /* files */ java.util.List.of("c:\\MyScript.vbs"), + /* ticket */ null, + /* auth */ singletonList(NTLM) +); +``` + +copies `c:\MyScript.vbs` to the host and runs the equivalent of: + +```text +CSCRIPT "C:\Windows\Temp\...\MyScript.vbs" +``` + +How the transfer works, and what to keep in mind: + +* Files travel **through the WinRM command shell itself** — chunked base64, decoded on the host with + `certutil` and verified with a digest. There is **no SMB**: TCP port 445 does not need to be + reachable, no administrative share is created, and the copy works from any client OS. +* The transfer is **content-addressed**: a fragment of the content digest is inserted before the + file extension (for example `MyScript.1a2b3c4d.vbs`), so files with the same name but different + content never overwrite each other. A script that inspects its own name (`WScript.ScriptName`) + therefore sees the digest fragment. +* A file already present on the host with an identical digest is **not transferred again**. +* The mechanism is designed for **small script files**, not bulk data — base64 over SOAP is not a + fast bulk transport. + +## Exceptions + +`execute(...)` declares: + +| Exception | When | +| --- | --- | +| `java.io.IOException` | An I/O error, including a copied-file problem. | +| `java.util.concurrent.TimeoutException` | The operation did not complete within `timeout`. | +| [`WindowsRemoteException`](apidocs/org/metricshub/winrm/exceptions/WindowsRemoteException.html) | Any problem on the remote host (in practice a [`WinRMException`](apidocs/org/metricshub/winrm/exceptions/WinRMException.html)). | + +See [Timeouts and Errors](timeouts-and-errors.html) for details. + +## From the command line + +The standalone jar runs a command with the `command` subcommand (aliases: `cmd`, `exec`, `run`). +Standard output and standard error are forwarded to the corresponding local streams, and the +process exits with the remote exit code when it fits in 0–255: + +```bash +java -jar ${project.artifactId}-${project.version}-standalone.jar \ + -h server.example.com -u 'DOMAIN\user' -pf password.txt --https \ + exec ipconfig /all +``` diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index ad29726..cfd50ab 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -1,71 +1,100 @@ +keywords: winrm java client, windows remote management, wsman, dependency-free, overview +description: A dependency-free Java client for Windows Remote Management (WinRM): run WQL queries and remote commands over NTLM or Kerberos. + # WinRM Java Client -The Windows Remote Management (WinRM) Java Client is a library that enables to: -* Connect to a remote Windows server using one of the two authentication types (NTLM, KERBEROS) -* Execute WMI Query Language (WQL) queries which uses HTTP/HTTPS protocols. - -> ## ⚠️ Upgrading from 1.x -> -> Version 2.0.0 **removed the legacy Apache CXF backend**: the dependency-free client is the only -> implementation (same public API). Unlike the CXF-based client, which silently trusted every TLS -> certificate, it **validates the server certificate and verifies the hostname by default**, so -> **WinRM-over-HTTPS connections to hosts with self-signed or untrusted certificates will fail** -> during the TLS handshake. To restore connectivity, either install the certificate into a Java -> trust store or set `-Dorg.metricshub.winrm.tls.insecure=true` (insecure — for testing only). -> The client supports NTLM over HTTP/HTTPS and Kerberos (SPNEGO) over HTTPS. Setting -> `-Dorg.metricshub.winrm.backend=cxf` now fails with a clear error; remove the property (or stay -> on winrm-java 1.x). - -# How to run the WinRM Client inside Java - -Add WinRM in the list of dependencies in your [Maven **pom.xml**](https://maven.apache.org/pom.html): - -```xml - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - - -``` + + +## Overview + +The **WinRM Java Client** is a small library that talks to the Windows Remote Management +(WS-Management) service on a remote Windows host. It lets a Java application: + +* run **WQL / WMI queries** such as `SELECT Name, State FROM Win32_Service` and read the rows back + ([WQL Queries](wql.html)), and +* **execute remote commands**, capturing standard output, standard error and the exit code — + optionally copying local script files to the host first ([Remote Commands](commands.html)). + +It supports **NTLM** over HTTP (with message encryption) and HTTPS, and **Kerberos (SPNEGO)** over +HTTPS ([Authentication](authentication.html)). + +Since 2.0.0 the client has **zero runtime dependencies** (no Apache CXF / JAX-WS / JAXB stack, no SMB +stack) and is immune by construction to JAXP `ServiceLoader` conflicts, because it uses the +JDK-default XML factories. Problems are reported through exceptions only — the library pulls in no +logging framework. + +> [!WARNING] +> **Upgrading from 1.x?** Version 2.0.0 removed the legacy Apache CXF backend and now +> **validates TLS certificates and verifies hostnames by default**. If you connect over HTTPS to +> hosts with self-signed certificates, read [Migrating from 1.x](migrating-from-1x.html) first. + +## Add the dependency + +The library is published on [Maven Central](https://central.sonatype.com/artifact/${project.groupId}/${project.artifactId}). -Use it as follows: -```Java -import static java.nio.file.Paths.get; +> [!TABS] +> * Maven +> ```xml +> +> ${project.groupId} +> ${project.artifactId} +> ${project.version} +> +> ``` +> * Gradle (Groovy) +> ```groovy +> implementation '${project.groupId}:${project.artifactId}:${project.version}' +> ``` +> * Gradle (Kotlin) +> ```kotlin +> implementation("${project.groupId}:${project.artifactId}:${project.version}") +> ``` + +See [Installation](installation.html) for the coordinates, the supported JDKs, and the standalone +command-line jar. + +## A first WQL query + +Everything starts with the static +[`WinRMWqlExecutor.executeWql(...)`](apidocs/org/metricshub/winrm/wql/WinRMWqlExecutor.html) method: + +```java import static java.util.Collections.singletonList; import static org.metricshub.winrm.WinRMHttpProtocolEnum.HTTP; import static org.metricshub.winrm.service.client.auth.AuthenticationEnum.NTLM; import static org.metricshub.winrm.wql.WinRMWqlExecutor.executeWql; -import java.nio.file.Path; -import java.util.List; -import java.util.concurrent.TimeoutException; - -import org.metricshub.winrm.exceptions.WinRMException; -import org.metricshub.winrm.exceptions.WqlQuerySyntaxException; -import org.metricshub.winrm.service.client.auth.AuthenticationEnum; import org.metricshub.winrm.wql.WinRMWqlExecutor; -public class Main { +public class Example { - public static void main(String[] args) throws WinRMException, WqlQuerySyntaxException, TimeoutException { + public static void main(String[] args) throws Exception { - final String wqlQuery = "SELECT Name, Path, Type FROM Win32_Share"; - final String hostname = "my-hostname-or-ip-address"; - final String username = "my-username"; - final char[] password = "my-password".toCharArray(); - final long timeout = 50 * 1000L; // in milliseconds - final Path ticketCache = get("path"); + WinRMWqlExecutor result = executeWql( + HTTP, // protocol (HTTP or HTTPS) + "server.example.com", // hostname (mandatory) + 5985, // port (null for the protocol default) + "DOMAIN\\Administrator", // username (DOMAIN\user or user) + "the-password".toCharArray(), // password + null, // namespace (null → ROOT\CIMV2) + "SELECT Name, State FROM Win32_Service", // WQL query + 30_000L, // timeout in milliseconds + null, // Kerberos ticket cache (null for NTLM) + singletonList(NTLM) // authentication schemes + ); - // Authentication type : NTLM or KERBEROS - final List authentications = singletonList(NTLM); - - // Execute a WQL Query in the hostname and print the result - executeWql(HTTP, hostname, 5985, username, password, null, wqlQuery, timeout, ticketCache, authentications) - .getRows().forEach(System.out::println); - - } + System.out.println(result.getHeaders()); // [Name, State] + result.getRows().forEach(System.out::println); // one List per row + } } ``` + +## Where to go next + +* [Installation](installation.html) — coordinates, supported JDKs, and the standalone CLI jar +* [WQL Queries](wql.html) — query WMI and read the result +* [Remote Commands](commands.html) — run commands and copy files to the host +* [Authentication](authentication.html) — NTLM and Kerberos +* [TLS / HTTPS](tls.html) — certificate validation and trust stores +* [Timeouts and Errors](timeouts-and-errors.html) — timeout semantics and the exception surface +* [Migrating from 1.x](migrating-from-1x.html) — the 2.0.0 breaking changes diff --git a/src/site/markdown/installation.md b/src/site/markdown/installation.md new file mode 100644 index 0000000..4a7461e --- /dev/null +++ b/src/site/markdown/installation.md @@ -0,0 +1,88 @@ +keywords: install, maven, gradle, dependency, standalone, cli, jdk +description: Add the WinRM Java Client to your build, or run the standalone command-line jar. + +# Installation + + + +## Coordinates + +The library is published on +[Maven Central](https://central.sonatype.com/artifact/${project.groupId}/${project.artifactId}) under: + +| Field | Value | +| --- | --- | +| `groupId` | `${project.groupId}` | +| `artifactId` | `${project.artifactId}` | +| `version` | `${project.version}` | + +## Add it to your build + +> [!TABS] +> * Maven +> ```xml +> +> ${project.groupId} +> ${project.artifactId} +> ${project.version} +> +> ``` +> * Gradle (Groovy) +> ```groovy +> implementation '${project.groupId}:${project.artifactId}:${project.version}' +> ``` +> * Gradle (Kotlin) +> ```kotlin +> implementation("${project.groupId}:${project.artifactId}:${project.version}") +> ``` + +For other build tools, the [dependency information](dependency-info.html) report lists the snippet +for Ivy, SBT, Leiningen, and others. + +## Supported JDKs + +The library targets **Java 11** and runs on any later JDK. + +## Runtime dependencies + +Since 2.0.0 the client has **zero runtime dependencies**. There is no longer an Apache CXF / +JAX-WS / JAXB stack, no BouncyCastle, and no SMB stack (`smbj`) on the classpath — the client speaks +WS-Management over the JDK's own HTTP and XML APIs, and copies files through the WinRM channel +itself. If you upgraded from 1.x, see [Migrating from 1.x](migrating-from-1x.html) for the details +and the behavior changes this implies. + +## Standalone command-line jar + +Every release also ships a self-contained executable jar that bundles the client and a small CLI. +Download `${project.artifactId}-${project.version}-standalone.jar` from the +[latest release](https://github.com/metricshub/winrm-java/releases/latest), then run it with Java: + +```bash +java -jar ${project.artifactId}-${project.version}-standalone.jar --help +``` + +Run a WQL query: + +```bash +java -jar ${project.artifactId}-${project.version}-standalone.jar \ + --hostname server.example.com --username 'DOMAIN\user' \ + --password-file password.txt --ntlm \ + wql 'SELECT Name, State FROM Win32_Service' +``` + +Run a remote command (`cmd`, `exec`, and `run` are aliases for `command`): + +```bash +java -jar ${project.artifactId}-${project.version}-standalone.jar \ + -h server.example.com -u Administrator -pf password.txt --https \ + exec ipconfig /all +``` + +The CLI is covered in more detail throughout the [Usage](wql.html) pages; `--version` prints the +build version. + +## Where to go next + +* [WQL Queries](wql.html) +* [Remote Commands](commands.html) +* [Authentication](authentication.html) diff --git a/src/site/markdown/migrating-from-1x.md b/src/site/markdown/migrating-from-1x.md new file mode 100644 index 0000000..797be58 --- /dev/null +++ b/src/site/markdown/migrating-from-1x.md @@ -0,0 +1,77 @@ +keywords: migration, upgrade, 1.x, 2.0, cxf, tls, smb, breaking changes +description: What changed in WinRM Java Client 2.0.0 and how to upgrade from the 1.x releases. + +# Migrating from 1.x + + + +Version 2.0.0 is a major cleanup: the legacy Apache CXF backend and the SMB-based file copy are +gone, leaving a **dependency-free** client. The **documented entry points are unchanged**, so typical +calling code is unaffected — but a few CXF/SMB-only public types were removed (see the *Removed types* +section below), and two runtime behaviors changed. Read this page before upgrading. + +## TL;DR + +* The Apache CXF backend was **removed**; the dependency-free client is the only implementation. +* HTTPS now **validates the certificate and verifies the hostname by default** (1.x trusted every + certificate). Self-signed hosts that used to work will now fail the TLS handshake until you trust + the certificate or opt out. +* File copy for `localFileToCopyList` now goes **through the WinRM channel** instead of SMB. +* A few CXF/SMB-only classes were removed. + +## TLS is validated by default + +The 1.x CXF client silently trusted every TLS certificate and skipped hostname verification. The +2.0.0 client uses the JDK's default validating socket factory instead, so **HTTPS connections to +hosts with self-signed or otherwise untrusted certificates now fail** during the handshake. + +To restore connectivity, either: + +* install the server certificate (or its issuing CA) into a Java trust store — + `-Djavax.net.ssl.trustStore=...` (recommended); or +* disable validation with `-Dorg.metricshub.winrm.tls.insecure=true` (**insecure — testing only**). + +See [TLS / HTTPS](tls.html) for details. + +## The CXF backend was removed + +The dependency-free client introduced in the previous release is now the only implementation. There +is no switch to fall back to the Apache CXF backend — if you still need it, stay on winrm-java 1.x. + +## File copy no longer uses SMB + +Files listed in `localFileToCopyList` for +[`WinRMCommandExecutor.execute(...)`](commands.html) are no longer copied over SMB. They are +transferred **through the WinRM command shell** (chunked base64, decoded on the host with `certutil` +and verified with a digest). The consequences: + +* **No SMB requirement** — TCP port 445 no longer needs to be reachable, no administrative or + temporary share is created on the host, and the copy now works from **any client OS** (1.x wrote + through a Windows UNC path and only worked from a Windows client). +* The remote copy is **content-addressed**: a fragment of the content digest is inserted before the + file extension (for example `script.1a2b3c4d.vbs`). Same-named files with different content can no + longer overwrite each other, and a script that reads its own name (`WScript.ScriptName`) will see + the digest fragment. +* A file already present on the host with an identical digest is not transferred again. +* The transport is meant for **small script files**, not bulk data. + +## Fewer dependencies + +Removing CXF and SMB leaves the library with **zero runtime dependencies**: the Apache CXF / +JAX-WS / JAXB stack is gone, and so are `smbj`, BouncyCastle, SLF4J, `mbassador`, and `asn-one`. The +standalone CLI jar shrinks from around 9 MB to a few hundred kB, and the library no longer references +any logging API — problems are reported through [exceptions](timeouts-and-errors.html) only. + +## Removed types + +These types and members were public in 1.x but are **removed** in 2.0.0. Code that referenced them +will not compile against 2.0.0 (all were CXF- or SMB-specific): + +* `KerberosCredentialsException` — was thrown only by CXF internals. +* `SmbTempShare` and `WindowsRemoteProcessUtils.copyLocalFilesToShare(...)` — replaced by the + WinRM-channel file transfer. `WindowsTempShare` is unchanged. +* The Apache CXF-based `WinRMService` and its `service.client` internals, along with the generated + WSDL/XSD resources. + +The documented entry points — `WinRMWqlExecutor`, `WinRMCommandExecutor`, `WinRMEndpoint`, +`WindowsRemoteCommandResult`, the enums, and the exception types — are unchanged. diff --git a/src/site/markdown/timeouts-and-errors.md b/src/site/markdown/timeouts-and-errors.md new file mode 100644 index 0000000..3c8ac57 --- /dev/null +++ b/src/site/markdown/timeouts-and-errors.md @@ -0,0 +1,71 @@ +keywords: timeout, exception, error, winrmexception, wsmanfault, exit code +description: Timeout semantics and the exception surface of the WinRM Java Client, plus the command-line exit codes. + +# Timeouts and Errors + + + +## Timeouts + +Both `executeWql(...)` and `WinRMCommandExecutor.execute(...)` take a `timeout` in +**milliseconds**. The value must be **greater than zero** — passing `0` or a negative value throws +an `IllegalArgumentException` immediately. + +The `timeout` applies to the remote operation and to the preparatory steps the client performs — +opening the connection, detecting the remote code page, and (for commands) copying files. It is +enforced **per step**, not as a single cumulative deadline: each major step is given up to `timeout` +to complete, so a call that chains several slow steps can take longer than one `timeout` overall +before a step finally exceeds its own limit and throws `java.util.concurrent.TimeoutException`. (When +files are copied, the later stages are budgeted against the time already spent, so that path stays +close to a single overall deadline.) + +```java +try { + executeWql(HTTP, host, null, user, password, null, query, 30_000L, null, singletonList(NTLM)); +} catch (java.util.concurrent.TimeoutException e) { + // the operation did not finish within 30 seconds +} +``` + +## The exception surface + +| Exception | Checked? | Meaning | +| --- | --- | --- | +| [`WindowsRemoteException`](apidocs/org/metricshub/winrm/exceptions/WindowsRemoteException.html) | yes | Base type for a problem on the remote host. | +| [`WinRMException`](apidocs/org/metricshub/winrm/exceptions/WinRMException.html) | yes | A WinRM/WSMan failure — authentication rejection, WMI error, protocol fault, connection or TLS problem. Extends `WindowsRemoteException`. | +| [`WqlQuerySyntaxException`](apidocs/org/metricshub/winrm/exceptions/WqlQuerySyntaxException.html) | yes | The WQL query does not match the supported `SELECT` syntax. | +| `java.util.concurrent.TimeoutException` | yes | The operation exceeded its `timeout`. | +| `java.io.IOException` | yes | Declared by `WinRMCommandExecutor.execute(...)` for I/O problems (including copied-file errors). | +| `IllegalArgumentException` | no | A mandatory argument is missing, or `timeout` is not greater than zero. | + +`executeWql(...)` declares `WinRMException`, `WqlQuerySyntaxException`, and `TimeoutException`. +`WinRMCommandExecutor.execute(...)` declares `IOException`, `TimeoutException`, and +`WindowsRemoteException`. + +### Fault detail + +When the remote host returns a WSMan fault, the exception message carries the detailed `WSManFault` +text — including the provider-level detail such as WMI `WBEM_E_*` mnemonics — alongside the SOAP +reason text, so the underlying cause is visible in the message. + +### Authentication failures + +A rejected credential surfaces as a `WinRMException` whose message is of the form +`Authentication error on with user name ""`. + +## Command-line exit codes + +The standalone jar maps outcomes to stable process exit codes: + +| Exit code | Meaning | +| ---: | --- | +| `0` | Successful WQL query or remote command. | +| `0`–`255` | Remote command exit code, when it fits in that range. | +| `64` | Invalid CLI usage. | +| `69` | Connection, DNS, socket, or TLS failure. | +| `70` | WinRM protocol or other remote failure. | +| `77` | Authentication failure. | +| `124` | Operation timeout. | + +Diagnostics are written only to standard error, so a WQL query's JSON Lines output on standard +output is never mixed with error messages. diff --git a/src/site/markdown/tls.md b/src/site/markdown/tls.md new file mode 100644 index 0000000..c79f841 --- /dev/null +++ b/src/site/markdown/tls.md @@ -0,0 +1,78 @@ +keywords: tls, https, certificate, trust store, hostname verification, insecure, self-signed +description: How the client validates TLS certificates over HTTPS, how to trust a certificate, and the insecure test-only opt-out. + +# TLS / HTTPS + + + +Use HTTPS by passing `HTTPS` as the protocol. HTTPS uses port **5986** by default (HTTP uses +**5985**); pass an explicit `port` to override either. + +```java +import static org.metricshub.winrm.WinRMHttpProtocolEnum.HTTPS; + +executeWql(HTTPS, "server.example.com", null, /* ... */); +``` + +## Validation is on by default + +Since 2.0.0 the client uses the JDK's default, **validating** `SSLSocketFactory`: it checks the +server certificate against the platform trust store and **verifies the server hostname** during the +handshake. + +> [!WARNING] +> This is a change from the 1.x CXF-based client, which silently trusted every certificate and +> skipped hostname verification. Connections over HTTPS to hosts with **self-signed or otherwise +> untrusted certificates now fail** during the TLS handshake unless you trust the certificate or +> explicitly opt out (below). See [Migrating from 1.x](migrating-from-1x.html). + +## Trusting a certificate + +The recommended fix for a self-signed or private-CA host is to add the server certificate (or its +issuing CA) to a Java trust store and point the JVM at it with the standard system properties: + +```bash +java -Djavax.net.ssl.trustStore=/path/to/truststore.jks \ + -Djavax.net.ssl.trustStorePassword=changeit \ + -cp ... MyApp +``` + +Because the client uses the JDK default socket factory, any trust store configured this way (or the +platform's default trust store) applies automatically. + +## Disabling validation (insecure — testing only) + +For a self-signed test host where installing a trust store is not practical, set the system property +`org.metricshub.winrm.tls.insecure` to `true`. This trusts **all** certificates and skips hostname +verification: + +```bash +java -Dorg.metricshub.winrm.tls.insecure=true -cp ... MyApp +``` + +> [!WARNING] +> This defeats the protection TLS provides against man-in-the-middle attacks. Use it only for +> testing or for isolated hosts, never in production. + +## On the command line + +The standalone jar mirrors this behavior: + +| Option | Meaning | +| --- | --- | +| `--https` | Use HTTPS (port 5986 by default). | +| `--https-permissive` | Trust any certificate and hostname. Intentionally insecure; testing only. Requires `--https`. | + +```bash +java -jar ${project.artifactId}-${project.version}-standalone.jar \ + -h server.example.com -u 'DOMAIN\user' -pf password.txt \ + --https --https-permissive \ + wql 'SELECT Name FROM Win32_ComputerSystem' +``` + +`--https-permissive` sets `org.metricshub.winrm.tls.insecure=true` for that invocation. + +## See also + +* [Authentication](authentication.html) — Kerberos requires HTTPS +* [Timeouts and Errors](timeouts-and-errors.html) — a TLS failure surfaces as a connection error diff --git a/src/site/markdown/wql.md b/src/site/markdown/wql.md new file mode 100644 index 0000000..0f553ff --- /dev/null +++ b/src/site/markdown/wql.md @@ -0,0 +1,124 @@ +keywords: wql, wmi, query, win32, namespace, root cimv2, cim +description: Run WQL / WMI queries with WinRMWqlExecutor and read the result rows. + +# WQL Queries + + + +WQL (WMI Query Language) is the SQL-like language used to query the Windows Management +Instrumentation (WMI) repository. The client runs a query on the remote host and returns the rows. + +## `WinRMWqlExecutor.executeWql(...)` + +A query is executed with the static method +[`WinRMWqlExecutor.executeWql(...)`](apidocs/org/metricshub/winrm/wql/WinRMWqlExecutor.html). It +opens a connection, runs the query, collects every row, closes the connection, and returns a +[`WinRMWqlExecutor`](apidocs/org/metricshub/winrm/wql/WinRMWqlExecutor.html) holding the result. + +```java +import static java.util.Collections.singletonList; +import static org.metricshub.winrm.WinRMHttpProtocolEnum.HTTP; +import static org.metricshub.winrm.service.client.auth.AuthenticationEnum.NTLM; +import static org.metricshub.winrm.wql.WinRMWqlExecutor.executeWql; + +import org.metricshub.winrm.wql.WinRMWqlExecutor; + +WinRMWqlExecutor result = executeWql( + HTTP, // protocol + "server.example.com", // hostname + null, // port (null → 5985 for HTTP, 5986 for HTTPS) + "DOMAIN\\Administrator", // username + "the-password".toCharArray(), // password + "ROOT\\CIMV2", // namespace (null → ROOT\CIMV2) + "SELECT Name, State FROM Win32_Service", // WQL query + 30_000L, // timeout in milliseconds + null, // Kerberos ticket cache (null for NTLM) + singletonList(NTLM) // authentication schemes +); +``` + +### Parameters + +| Parameter | Type | Notes | +| --- | --- | --- | +| `protocol` | [`WinRMHttpProtocolEnum`](apidocs/org/metricshub/winrm/WinRMHttpProtocolEnum.html) | `HTTP` or `HTTPS`. `null` defaults to `HTTP`. | +| `hostname` | `String` | Host name or IP address. **Mandatory.** | +| `port` | `Integer` | `null` uses the protocol default (5985 for HTTP, 5986 for HTTPS). | +| `username` | `String` | `DOMAIN\user` or `user`. **Mandatory.** See [Authentication](authentication.html). | +| `password` | `char[]` | The password. | +| `namespace` | `String` | WMI namespace. `null` or blank defaults to `ROOT\CIMV2`. Backslashes and forward slashes are both accepted. | +| `wqlQuery` | `String` | The WQL query. **Mandatory.** | +| `timeout` | `long` | Timeout in milliseconds. Must be **greater than zero** (an `IllegalArgumentException` is thrown otherwise). See [Timeouts and Errors](timeouts-and-errors.html). | +| `ticketCache` | `java.nio.file.Path` | Kerberos ticket cache path. `null` for NTLM. See [Authentication](authentication.html). | +| `authentications` | `List<`[`AuthenticationEnum`](apidocs/org/metricshub/winrm/service/client/auth/AuthenticationEnum.html)`>` | Requested schemes. `null` or empty means NTLM only. | + +## Reading the result + +The returned [`WinRMWqlExecutor`](apidocs/org/metricshub/winrm/wql/WinRMWqlExecutor.html) exposes: + +| Method | Returns | Description | +| --- | --- | --- | +| `getHeaders()` | `List` | The property (column) names. | +| `getRows()` | `List>` | One `List` per row, with values in the same order as the headers. | +| `getExecutionTime()` | `long` | Wall-clock time of the whole call, in milliseconds. | + +```java +List headers = result.getHeaders(); // e.g. [Name, State] +for (List row : result.getRows()) { + System.out.println(headers + " = " + row); +} +``` + +### Property order and case + +* When you select explicit properties (`SELECT Name, State FROM ...`), the headers keep the **order + of the query** and the **exact case reported by WMI**. +* With `SELECT * FROM ...`, the properties are returned in **alphabetical order** (case-insensitive). +* If the query returns no rows, the headers fall back to the property names exactly as written in + the query (WMI's own casing cannot be recovered from an empty result set). + +## Supported WQL syntax + +The client validates that the query is a simple `SELECT`: + +```sql +SELECT * FROM Win32_OperatingSystem +SELECT Name, State, StartMode FROM Win32_Service +SELECT Name FROM Win32_Process WHERE Name = 'explorer.exe' +``` + +The grammar is a single `SELECT` of either `*` or a comma-separated property list, a `FROM` clause, +and an optional `WHERE` clause. Joins, sub-selects, and other advanced constructs are not part of +the supported syntax. An invalid query raises a +[`WqlQuerySyntaxException`](apidocs/org/metricshub/winrm/exceptions/WqlQuerySyntaxException.html). + +## Choosing a namespace + +Most Windows classes live under the default `ROOT\CIMV2` namespace. To query a different one — for +example `ROOT\Microsoft\SqlServer` or `ROOT\WMI` — pass it as the `namespace` argument. Both +`ROOT\WMI` and `ROOT/WMI` are accepted. + +## Exceptions + +`executeWql(...)` declares three checked exceptions: + +| Exception | When | +| --- | --- | +| [`WqlQuerySyntaxException`](apidocs/org/metricshub/winrm/exceptions/WqlQuerySyntaxException.html) | The query does not match the supported `SELECT` syntax. | +| [`WinRMException`](apidocs/org/metricshub/winrm/exceptions/WinRMException.html) | Any WinRM/WSMan problem on the remote host (authentication, WMI error, protocol fault, ...). | +| `java.util.concurrent.TimeoutException` | The operation did not complete within `timeout`. | + +See [Timeouts and Errors](timeouts-and-errors.html) for the full exception surface. + +## From the command line + +The standalone jar exposes the same capability through the `wql` subcommand, printing one compact +UTF-8 JSON object per row ([JSON Lines](https://jsonlines.org/)): + +```bash +java -jar ${project.artifactId}-${project.version}-standalone.jar \ + -h server.example.com -u 'DOMAIN\user' -pf password.txt --ntlm \ + wql 'SELECT Name, State FROM Win32_Service' +``` + +Property order follows the WinRM response; diagnostics go only to standard error. diff --git a/src/site/resources/css/site.css b/src/site/resources/css/site.css index e85911b..24a024c 100644 --- a/src/site/resources/css/site.css +++ b/src/site/resources/css/site.css @@ -64,7 +64,7 @@ --heading-font: "Poppins", sans-serif; --content-font: "Poppins", sans-serif; - --content-font-size: medium; + --content-font-size: 15px; --banner-font-size: 40px; --banner-font-weight: 800; diff --git a/src/site/site.xml b/src/site/site.xml index d042bbf..c4a25f5 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -1,36 +1,71 @@ - - + + org.sentrysoftware.maven sentry-maven-skin - 6.4.01 + 8.0.00 true - winrm + winrm, wql, wmi, windows, ntlm, kerberos, java, remote management + + + WinRM Java Client on GitHub + https://github.com/metricshub/winrm-java + fa-brands fa-github + + + + + Issue Tracker + https://github.com/metricshub/winrm-java/issues + + + Licenses + licenses.html + + + Releases + https://github.com/metricshub/winrm-java/releases + + - - images/metricshub-logo-only.png - https://metricshub.org + + MetricsHub - + + + - - - - + + + + + + + + + + + + + + + + - \ No newline at end of file +