From aa0f4346f2c949165455937d958b065ae11bd85f Mon Sep 17 00:00:00 2001 From: NopeCharon <115794254+NopeCharon@users.noreply.github.com> Date: Wed, 28 Oct 2026 02:42:15 +0530 Subject: [PATCH 1/2] fix(workloadapi): accept IPv6 literals in tcp Workload API addresses The SPIFFE Workload Endpoint standard requires the host of a tcp:// socket address to be an IP address, which includes IPv6. An IPv6 literal in a URI has to be written in square brackets (tcp://[::1]:8000), and java.net.URI.getHost() returns it with the brackets kept ("[::1]"). Address.validateTcpAddress passed that value to Guava's InetAddresses.isInetAddress, which does not accept brackets. So every IPv6 address was rejected with "host component must be an IP:port", and DefaultWorkloadApiClient, DefaultX509Source, DefaultJwtSource and the helper could not connect to an agent listening on IPv6. (Before commons-validator was removed, its InetAddressValidator rejected bracketed hosts in the same way.) Use InetAddresses.isUriInetAddress instead. It accepts IPv4 literals and bracketed IPv6 literals, which is exactly what URI.getHost() returns, and still rejects hostnames. The gRPC channel factories already pass the bracketed host to NettyChannelBuilder.forAddress, which resolves it correctly, so no change is needed there. AddressTest gains valid IPv6 cases (loopback and a global address) and an IPv6 address without a port, which is still rejected. Signed-off-by: NopeCharon <115794254+NopeCharon@users.noreply.github.com> --- .../src/main/java/io/spiffe/workloadapi/Address.java | 3 ++- .../src/test/java/io/spiffe/workloadapi/AddressTest.java | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/Address.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/Address.java index a76a244d..ec8c33b3 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/Address.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/Address.java @@ -157,8 +157,9 @@ private static void validateTcpAddress(final URI parsedAddress) throws SocketEnd throw new SocketEndpointAddressException(String.format(error, parsedAddress)); } + // URI.getHost() returns IPv6 literals enclosed in square brackets (e.g. '[::1]') boolean ipValid; - ipValid = InetAddresses.isInetAddress(parsedAddress.getHost()); + ipValid = InetAddresses.isUriInetAddress(parsedAddress.getHost()); if (!ipValid) { String error = "Workload endpoint tcp socket URI host component must be an IP:port: %s"; throw new SocketEndpointAddressException(String.format(error, parsedAddress)); diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/AddressTest.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/AddressTest.java index ff6f0b6e..efbd1cbb 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/AddressTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/AddressTest.java @@ -46,6 +46,8 @@ static Stream provideTestAddress() { Arguments.of("unix:/path/to/endpoint.sock", URI.create("unix:/path/to/endpoint.sock")), Arguments.of("unix:///path/to/endpoint.sock", URI.create("unix:///path/to/endpoint.sock")), Arguments.of("tcp://127.0.0.1:8000", URI.create("tcp://127.0.0.1:8000")), + Arguments.of("tcp://[::1]:8000", URI.create("tcp://[::1]:8000")), + Arguments.of("tcp://[2001:db8::1]:8000", URI.create("tcp://[2001:db8::1]:8000")), Arguments.of("", "Workload endpoint socket URI must have a tcp:// or unix:// scheme: "), Arguments.of("\\t", "Workload endpoint socket is not a valid URI: \\t"), @@ -69,7 +71,9 @@ static Stream provideTestAddress() { Arguments.of("tcp://1.2.3.4:5#whatever", "Workload endpoint tcp socket URI must not include a fragment: tcp://1.2.3.4:5#whatever"), Arguments.of("tcp://john:doe@1.2.3.4:5/path", "Workload endpoint tcp socket URI must not include user info: tcp://john:doe@1.2.3.4:5/path"), Arguments.of("tcp://foo:9000", "Workload endpoint tcp socket URI host component must be an IP:port: tcp://foo:9000"), - Arguments.of("tcp://1.2.3.4", "Workload endpoint tcp socket URI host component must include a port: tcp://1.2.3.4") + Arguments.of("tcp://[foo]:9000", "Workload endpoint socket is not a valid URI: tcp://[foo]:9000"), + Arguments.of("tcp://1.2.3.4", "Workload endpoint tcp socket URI host component must include a port: tcp://1.2.3.4"), + Arguments.of("tcp://[::1]", "Workload endpoint tcp socket URI host component must include a port: tcp://[::1]") ); } From b34da7dcb2f9be820124eb8ae2ed1f6eff4c3b7c Mon Sep 17 00:00:00 2001 From: NopeCharon <115794254+NopeCharon@users.noreply.github.com> Date: Thu, 29 Oct 2026 21:41:32 +0530 Subject: [PATCH 2/2] fix(workloadapi): reject IPv6 zone IDs in tcp Workload API addresses InetAddresses.isUriInetAddress ignores a zone suffix, so a host such as [fe80::1%eth0] passed validation and was handed to gRPC unchanged, where the scope could be misread (e.g. %25eth0 as interface "25eth0"). RFC 3986, which the Workload Endpoint standard references, does not allow zone IDs, so reject hosts that contain '%'. AddressTest gains rejected cases for zone IDs and an accepted case for an IPv4-mapped IPv6 address. Signed-off-by: NopeCharon <115794254+NopeCharon@users.noreply.github.com> --- .../src/main/java/io/spiffe/workloadapi/Address.java | 7 ++++--- .../src/test/java/io/spiffe/workloadapi/AddressTest.java | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/Address.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/Address.java index ec8c33b3..9c208e85 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/Address.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/Address.java @@ -157,9 +157,10 @@ private static void validateTcpAddress(final URI parsedAddress) throws SocketEnd throw new SocketEndpointAddressException(String.format(error, parsedAddress)); } - // URI.getHost() returns IPv6 literals enclosed in square brackets (e.g. '[::1]') - boolean ipValid; - ipValid = InetAddresses.isUriInetAddress(parsedAddress.getHost()); + // URI.getHost() returns IPv6 literals enclosed in square brackets (e.g. '[::1]'). + // Zone IDs (e.g. '[fe80::1%eth0]') are not allowed by RFC 3986, so hosts containing '%' are rejected. + String host = parsedAddress.getHost(); + boolean ipValid = host.indexOf('%') < 0 && InetAddresses.isUriInetAddress(host); if (!ipValid) { String error = "Workload endpoint tcp socket URI host component must be an IP:port: %s"; throw new SocketEndpointAddressException(String.format(error, parsedAddress)); diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/AddressTest.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/AddressTest.java index efbd1cbb..28037097 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/AddressTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/AddressTest.java @@ -48,6 +48,7 @@ static Stream provideTestAddress() { Arguments.of("tcp://127.0.0.1:8000", URI.create("tcp://127.0.0.1:8000")), Arguments.of("tcp://[::1]:8000", URI.create("tcp://[::1]:8000")), Arguments.of("tcp://[2001:db8::1]:8000", URI.create("tcp://[2001:db8::1]:8000")), + Arguments.of("tcp://[::ffff:127.0.0.1]:8000", URI.create("tcp://[::ffff:127.0.0.1]:8000")), Arguments.of("", "Workload endpoint socket URI must have a tcp:// or unix:// scheme: "), Arguments.of("\\t", "Workload endpoint socket is not a valid URI: \\t"), @@ -72,6 +73,8 @@ static Stream provideTestAddress() { Arguments.of("tcp://john:doe@1.2.3.4:5/path", "Workload endpoint tcp socket URI must not include user info: tcp://john:doe@1.2.3.4:5/path"), Arguments.of("tcp://foo:9000", "Workload endpoint tcp socket URI host component must be an IP:port: tcp://foo:9000"), Arguments.of("tcp://[foo]:9000", "Workload endpoint socket is not a valid URI: tcp://[foo]:9000"), + Arguments.of("tcp://[fe80::1%eth0]:8000", "Workload endpoint tcp socket URI host component must be an IP:port: tcp://[fe80::1%eth0]:8000"), + Arguments.of("tcp://[fe80::1%25eth0]:8000", "Workload endpoint tcp socket URI host component must be an IP:port: tcp://[fe80::1%25eth0]:8000"), Arguments.of("tcp://1.2.3.4", "Workload endpoint tcp socket URI host component must include a port: tcp://1.2.3.4"), Arguments.of("tcp://[::1]", "Workload endpoint tcp socket URI host component must include a port: tcp://[::1]") );