9 Securing gRPC Client Connections

Require the intended transport, authentication, serializer, and diagnostic policies on the gRPC proxy and its clients.

A gRPC connection has independent transport, identity, and operation-policy choices. Anonymous compatibility remains available unless authentication is configured. A global identity asserter alone does not make a gRPC acceptor require authentication. Configure the acceptor's authentication method explicitly and make its asserter return a verified non-null subject.

Control Setting Verification
Server TLS policy coherence.grpc.server.secure-transport=required or acceptor XML Cleartext and insecure providers cannot satisfy the policy.
Java client TLS policy coherence.grpc.channels.<name>.secure-transport=required or channel XML The client trusts the intended issuer and verifies the server name.
Authentication coherence.grpc.auth=basic or <auth-method>basic</auth-method> Invalid credentials fail and valid credentials establish the expected subject.
Operation authorization Configured service/storage authorizer An authenticated but unauthorized operation is denied.

Use Configuring a Secure gRPC Proxy with Connecting a Java gRPC Client Securely. Serializer names and diagnostic exposure are covered in gRPC Serializer and Diagnostic Policy.

Client scope

Use a released Coherence client and supported transport implementation. Before adapting the Java recipe to Go, Python, JavaScript, or another language, verify that client's trust-material, credential, hostname/authority, and mTLS APIs against the intended server. Secure-required servers should continue rejecting insecure clients while client adoption work is completed.

Configuring a Secure gRPC Proxy

Configure an SSL socket provider, explicit authentication, and required transport on a gRPC acceptor.

Define server-tls in the member operational override using Configuring TLS with Protected Password Sources. Server-authenticated TLS requires a server identity. mTLS also requires trust and client certificates. Provision the certificate for the actual DNS name used by clients.

Place this acceptor fragment in the proxy scheme in cache configuration. It requires the Coherence gRPC proxy module and an identity asserter accepting UsernameAndPassword, such as Authenticating Credentials with JAAS.

<acceptor-config>
  <grpc-acceptor>
    <local-address>
      <address>0.0.0.0</address>
      <port>1408</port>
    </local-address>
    <socket-provider>server-tls</socket-provider>
    <auth-method>basic</auth-method>
    <channelz>disabled</channelz>
    <error-disclosure>safe</error-disclosure>
    <secure-transport>required</secure-transport>
  </grpc-acceptor>
</acceptor-config>

The wildcard bind is appropriate only with the required network controls; use a specific interface when possible. The recipe pairs Basic authentication with required TLS. Basic alone does not impose a startup-time TLS mandate. Hardened policy rejects Basic requests over cleartext; compatibility permits them with a warning.

Subject propagation

The proxy preserves authenticated subjects through supported v0 direct/async and v1 subchannel/cache-protocol paths for downstream authorization. Configure the operational identity asserter and subject scoping appropriate to the application, and wire a storage authorizer to every scheme needing operation checks.

Verification

  1. Connect with the configured CA and matching server name, authenticate, and complete an authorized operation.

  2. Test an untrusted CA, a name mismatch, and a cleartext channel. Each must fail at the relevant transport boundary.

  3. Try missing and invalid Basic credentials. Expect UNAUTHENTICATED.

  4. With valid credentials for a denied role, try the protected operation. Expect PERMISSION_DENIED.

  5. Confirm Channelz is unavailable and safe errors omit internal stacks while retaining bounded status information.

Connecting a Java gRPC Client Securely

Configure the product Java client with explicit trust, required transport, and credentials for the selected acceptor.

Define client-tls in the client operational override. Server-authenticated TLS needs trust material; mTLS also needs the client's private key and certificate. Connect using a name present in the server certificate. Map application cache names to secure-grpc, and add this scheme:

<remote-grpc-cache-scheme>
  <scheme-name>secure-grpc</scheme-name>
  <service-name>SecureGrpcClient</service-name>
  <grpc-channel>
    <remote-addresses>
      <socket-address>
        <address>cache.example.com</address>
        <port>1408</port>
      </socket-address>
    </remote-addresses>
    <socket-provider>client-tls</socket-provider>
    <secure-transport>required</secure-transport>
    <configurer>
      <class-name>com.example.security.BasicGrpcConfigurer</class-name>
    </configurer>
  </grpc-channel>
</remote-grpc-cache-scheme>

The following complete configurer uses the product GrpcChannelConfigurer and gRPC metadata utilities. It requires Java 21 or later and the product gRPC client dependencies. Its password URL identifies an OS-protected secret file; the password itself is not a system-property value.

package com.example.security;

import com.oracle.coherence.grpc.client.common.GrpcChannelConfigurer;
import com.tangosol.net.PasswordProvider;
import com.tangosol.net.URLPasswordProvider;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Objects;

public final class BasicGrpcConfigurer implements GrpcChannelConfigurer {
    @Override
    public void apply(ManagedChannelBuilder<?> builder) {
        var username = Objects.requireNonNull(
                System.getProperty("example.grpc.username"), "username");
        var url = Objects.requireNonNull(
                System.getProperty("example.grpc.password.url"), "password URL");
        char[] password = new URLPasswordProvider(url, true).get();
        try {
            if (username.isBlank() || username.contains(":")
                    || password == null || password.length == 0) {
                throw new IllegalArgumentException("Missing or invalid credentials");
            }
            var value = username + ":" + new String(password);
            var encoded = Base64.getEncoder().encodeToString(
                    value.getBytes(StandardCharsets.UTF_8));
            var metadata = new Metadata();
            metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER),
                    "Basic " + encoded);
            builder.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata));
        } finally {
            PasswordProvider.reset(password);
        }
    }
}

Set example.grpc.username and example.grpc.password.url before channel creation. This minimal configurer reads credentials once. Its Basic header remains in memory for the channel lifetime; resetting the source array does not erase immutable strings. Recreate the channel after rotating this secret, or use an application integration designed for credential refresh. Never log the metadata or include it in a URL.

Bootstrap and session use

Select the operational override and client cache configuration before startup, and deploy the client module/configurer and application security resources. The application uses the standard bootstrap and typed session APIs:

package com.example.security;

import com.tangosol.net.Coherence;
import com.tangosol.net.NamedMap;

public final class SecureClient {
    public static void main(String[] args) {
        try (var coherence = Coherence.client().start().join()) {
            var session = coherence.getSession();
            NamedMap<String, String> accounts = session.getMap("accounts");
            System.out.println(accounts.get("account-1"));
        }
    }
}

Keep the instance and session for the application lifetime. A successful read proves that read path; separately test a denied write and a server-name mismatch. Do not use a permissive hostname verifier or authority override to hide a certificate error.

gRPC Serializer and Diagnostic Policy

Constrain client-selected serialization and choose explicit diagnostic exposure for the gRPC service.

Starting with Coherence 26.1.0.0, an unset coherence.security.mode selects the hardened column below. With their own settings left at auto, Channelz is disabled and error disclosure is safe. Select the compatibility profile explicitly to obtain its different defaults.

Setting Compatibility profile (explicit) Hardened profile (default starting with 26.1.0.0)
Empty serializer format; built-in pof, json, genson, Coherence JsonSerializer name Accepted according to serializer configuration. Accepted according to serializer configuration.
java, DefaultSerializer, arbitrary custom names Would-reject telemetry outside the allowed set. Require coherence.grpc.serializer.allowlist.
Channelz auto Enabled. Disabled.
Error disclosure auto Diagnostic detail. Safe disclosure.

An omitted/empty format selects the default. The literal string default is not in the built-in allowed-name set. A custom name must resolve to its configured implementation and be explicitly approved where required. Serializer-name approval does not authorize every materialized class or executable.

JSON @class and @type metadata is gated before materialization, including nested and statically typed gRPC paths. Configure supported aliases and application classes; an application allowlist does not bypass independent JSON metadata policy.

-Dcoherence.grpc.channelz=disabled
-Dcoherence.grpc.error-disclosure=safe

Explicit values override auto. Diagnostic mode can disclose internal messages, stack metadata, and serialized throwable details; restrict its use appropriately. Channelz policy does not remove normal health-service registration.

Treat a serializer INVALID_ARGUMENT as input/configuration failure, UNAUTHENTICATED as identity failure, and PERMISSION_DENIED as policy failure. Supported listener streams preserve permission-denied semantics for security errors. Use server-side bounded signals when safe disclosure omits cause details.