Transparent Proxy
The SAP Cloud SDK supports connecting to systems through the Transparent Proxy, which provides enhanced connectivity features and simplified on-premise access in Kubernetes-based environments.
The TransparentProxyDestination class enables applications to leverage these capabilities seamlessly.
Prerequisites
The Transparent Proxy is currently only available for Kubernetes-based environments.
Before using TransparentProxyDestination, ensure that the Transparent Proxy is installed and configured in your Kubernetes cluster.
Refer to the official installation guide for setup instructions.
The Transparent Proxy is available in Kyma environment as a dedicated module.
Refer to Transparent Proxy in Kyma environment for instructions how to add the module.
Background Information
The Transparent Proxy is a SAP BTP Connectivity component that acts as an intermediary between your application and target systems. The Transparent Proxy simplifies and unifies how your Kubernetes workloads connect to remote systems using SAP BTP destinations. It provides authentication, principal propagation, SOCKS5 handshake, and easy access to the destination target systems by exposing them as Kubernetes Services.
The Transparent Proxy obtains relevant destination configuration from the SAP Destination service. It uses this destination to forward the request to the target system or to the Connectivity Proxy for On-Premise destinations. Consequently, your app does not interact with SAP Destination service or Connectivity service. This means your application do not require bindings to these two services, everything is handled by the Transparent Proxy.
TransparentProxyDestination Builder
The TransparentProxyDestination class provides explicit destination builders for direct control over transparent proxy connections.
This approach gives you fine-grained control over individual destination configurations.
Creating Destinations
Concrete SAP Destination
Allows you to connect to a concrete SAP destination. Setting generic headers is allowed but dynamic properties like destination name or fragments is not. As a prerequisite, you have to create a Destination Custom Resource inside the Kubernetes cluster. For more information how to use the Transparent Proxy, refer to Using the Transparent Proxy.
TransparentProxyDestination destination = TransparentProxyDestination
.destination(<destination-custom-resource-name>.<destination-custom-resource-namespace>)
.header("X-Custom-Header", "custom-value")
.property("some-property-key", "some-value")
.build();
<destination-custom-resource-namespace> can be omitted if the destination custom resource is created in the same namespace as the application workload.
Gateway
Allows you to connect to arbitrary SAP destinations you have access to. As a prerequisite, you have to create a Gateway Destination Custom Resource inside the Kubernetes cluster. For more information how to use the Transparent Proxy for this concrete scenario, refer to Destination Gateway.
TransparentProxyDestination destination = TransparentProxyDestination
.gateway("my-destination", <destination-custom-resource-name>.<destination-custom-resource-namespace>)
.fragmentName("my-fragment")
.build();
Configuration Options
Tenant Settings
The SAP Cloud SDK automatically sets the current tenant as tenant ID on a per-request basis.
In case a fixed tenant should be used, you can manually set it as follows:
For Concrete Destinations:
// Using a fixed tenant ID (automatically set by SAP Cloud SDK if not specified)
// alternatively, .tenantSubdomain("..") can be used, but only one of the two options may be set
TransparentProxyDestination destination = TransparentProxyDestination
.destination(<destination-custom-resource-name>.<destination-custom-resource-namespace>)
.tenantId("my-tenant-id")
.build();
For Gateway Destinations:
// Using a fixed tenant ID (automatically set by SAP Cloud SDK if not specified)
// alternatively, .tenantSubdomain("..") can be used, but only one of the two options may be set
TransparentProxyDestination destination = TransparentProxyDestination
.gateway("my-destination", <destination-custom-resource-name>.<destination-custom-resource-namespace>)
.tenantId("my-tenant-id")
.build();
Authorization Header Configuration
The SAP Cloud SDK automatically sets the current user's authorization token in the Authorization header on a per-request basis.
In case a fixed authorization token should be used, you can manually set it as follows:
For Concrete Destinations:
TransparentProxyDestination destination = TransparentProxyDestination
.destination(<destination-custom-resource-name>.<destination-custom-resource-namespace>)
.authorization("Bearer my-token")
.build();
For Gateway Destinations:
TransparentProxyDestination destination = TransparentProxyDestination
.gateway("my-destination", <destination-custom-resource-name>.<destination-custom-resource-namespace>)
.authorization("Bearer my-token")
.build();
Note: When you manually set authorization, it will override the SAP Cloud SDK automatic token handling.
Migration from Traditional Destinations
Migrating from traditional destination configurations:
Before (Traditional Destination)
Destination destination = DestinationAccessor.getDestination("my-destination");
HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination);
After (Transparent Proxy - Gateway)
TransparentProxyDestination destination = TransparentProxyDestination
.gateway("my-destination", <destination-custom-resource-name>.<destination-custom-resource-namespace>)
.build();
HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination);
After (Transparent Proxy - Concrete Destination)
TransparentProxyDestination destination = TransparentProxyDestination
.destination(<destination-custom-resource-name>.<destination-custom-resource-namespace>)
.build();
HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination);
Register Transparent Proxy for destination access
The TransparentProxy class provides a DestinationLoader that enables routing destination traffic via the DestinationAccessor through a single registered gateway host.
This approach provides centralized proxy configuration where all destination requests are automatically routed through the configured gateway without requiring explicit destination builders.
It works only for SAP Cloud SDKDestinationAccessor approach.
When to Use This Approach
- You want all destinations to automatically route through a single Dynamic Destination Custom Resource
- You prefer a centralized, "set-it-and-forget-it" configuration approach
- You have many destinations that should all use the same proxy configuration
- You want to minimize code changes when migrating from traditional destination access
- For CAP applications as CAP uses DestinationAccessor under the hood
Registration Methods
Register with Default Port
Register a transparent proxy gateway using the default port 80:
// Register with default port 80
// Note: <destination-gateway-host> - the host is a combination of the Gateway Destination Custom Resource name and namespace
TransparentProxy.register("<destination-gateway-host>");
The host will be verified if it is reachable and must not contain any path components. If no scheme is provided, HTTP will be used by default.
Register with Custom Port
Register a transparent proxy gateway with a specified port:
// Register with custom port when using a non-default port defined in the Destination CR's .service.port field
TransparentProxy.register("http://<destination-gateway-host>", <port>);
The final URI will be constructed as: <normalized-host>:<port>
Register with Provider Tenant Identifier
In scenarios where tenant information may not be readily available during destination requests, you can register a transparent proxy with a provider tenant ID that serves as a fallback:
// Register with default port 80 and provider tenant ID
TransparentProxy.register("<destination-gateway-host>", "provider-tenant-id");
// Or register with custom port and provider tenant ID
TransparentProxy.register("http://<destination-gateway-host>", 8080, "provider-tenant-id");
::note Transparent Proxy requires an explicit tenant ID The provider tenant identifier is required for requests where no tenant is explicitly available in the ThreadContext. Typically this is the case when testing locally without authentication or when running scheduled tasks.
Usage Example
// Register the transparent proxy during application initialization
TransparentProxy.register("<destination-gateway-host>", 8080);
// All subsequent destination requests will be routed through the registered gateway
// Note: returns TransparentProxyDestination which implements Destination
Destination destination = DestinationAccessor.getDestination("my-destination");
Advanced Usage with DestinationOptions
The Transparent Proxy Loader supports advanced configuration through DestinationOptions, allowing you to customize various aspects of the destination lookup and authentication.
This is especially useful when you need to:
- Configure fragments for destination configurations
- Override tenant information
- Provide custom authentication tokens or assertions
- Configure OAuth flows
- Set up destination chains
- Control destination and fragment lookup levels
Basic Usage with Options
// Register the transparent proxy
TransparentProxy.register("<destination-gateway-host>", 8080);
// Create options and retrieve destination
DestinationOptions options = DestinationServiceOptionsAugmenter.augmenter()
.fragmentName("my-fragment")
.build();
Destination destination = DestinationAccessor.getDestination("my-destination", options);
Available DestinationOptions Parameters
The following parameters can be used with DestinationServiceOptionsAugmenter.augmenter() .customHeaders(new Header(key, value)):
Fragment Configuration
-
x-fragment-name: Specifies the name of a fragment to apply to the destination configuration.fragmentName("my-fragment") -
x-fragment-optional: Controls whether the fragment is optional (default: false). Set to "true" or "false".customHeaders(new Header("x-fragment-optional", "true"))
Authentication Configuration
-
x-client-assertion: Provides a client assertion for OAuth flows.customHeaders(new Header("x-client-assertion", "eyJhbGciOiJSUzI1NiIs...")) -
x-client-assertion-type: Specifies the client assertion type.customHeaders(new Header("x-client-assertion-type",
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer")) -
x-client-assertion-destination-name: References another destination for client assertion.customHeaders(new Header("x-client-assertion-destination-name",
"assertion-destination"))
Token Exchange Configuration
-
x-subject-token-type: Specifies the subject token type for token exchange.customHeaders(new Header("x-subject-token-type",
"urn:ietf:params:oauth:token-type:jwt")) -
x-actor-token: Provides an actor token for token exchange flows.customHeaders(new Header("x-actor-token", "actor-token-value")) -
x-actor-token-type: Specifies the actor token type.customHeaders(new Header("x-actor-token-type",
"urn:ietf:params:oauth:token-type:jwt"))
OAuth Flow Configuration
-
x-redirect-uri: Specifies the redirect URI for OAuth flows.customHeaders(new Header("x-redirect-uri",
"https://my-app.com/callback")) -
x-code-verifier: Provides the PKCE code verifier for OAuth flows.customHeaders(new Header("x-code-verifier", "code-verifier-123"))
Destination Chain Configuration
-
x-chain-name: Specifies the name of a destination chain.customHeaders(new Header("x-chain-name", "my-chain")) -
x-chain-var-subjectToken: Provides a subject token variable for chains.customHeaders(new Header("x-chain-var-subjectToken",
"chain-subject-token")) -
x-chain-var-subjectTokenType: Specifies the subject token type for chains.customHeaders(new Header("x-chain-var-subjectTokenType",
"urn:ietf:params:oauth:token-type:jwt")) -
x-chain-var-samlProviderDestinationName: References a SAML provider destination in chains.customHeaders(new Header("x-chain-var-samlProviderDestinationName",
"saml-provider-dest"))
Level Configuration
-
x-destination-level: Specifies the lookup level for the destination (e.g., "subaccount", "INSTANCE").customHeaders(new Header("x-destination-level", "subaccount")) -
x-fragment-level: Specifies the lookup level for the fragment (e.g., "subaccount", "INSTANCE").customHeaders(new Header("x-fragment-level", "INSTANCE"))
Complete Example with Multiple Options
// Register the transparent proxy
TransparentProxy.register("<destination-gateway-host>", 8080);
// Configure comprehensive options
DestinationOptions options = DestinationOptions.builder()
// Fragment configuration
.customHeaders(new Header("x-fragment-name", "production-fragment"))
.customHeaders(new Header("x-fragment-optional", "false"))
// Tenant configuration
.customHeaders(new Header("x-tenant-id", "tenant-123"))
.customHeaders(new Header("x-token-service-tenant", "service-tenant"))
// Authentication
.customHeaders(new Header("authorization", "Bearer custom-token"))
// Level configuration
.customHeaders(new Header("x-fragment-level", "INSTANCE"))
// Retrieve destination with options
Destination destination = DestinationAccessor.getDestination("my-destination", options);
// Use the destination for your requests
HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination);
When using DestinationOptions parameters:
- Parameters in DestinationOptions take precedence over provider tenant ID set during registration
- Parameters in DestinationOptions override automatic tenant resolution from context
- The SAP Cloud SDK automatically handles current user tokens unless overridden via
AUTHORIZATION_HEADER_KEY
You cannot use both TENANT_ID_HEADER_KEY and TENANT_SUBDOMAIN_HEADER_KEY simultaneously.
Choose one based on your requirements.
Troubleshooting
Common Issues
- Missing destination name for gateway: Ensure the destination name is provided as the first parameter to
.gateway(<destination-name>, <destination-custom-resource-name>.<destination-custom-resource-namespace>) - Tenant context not available: Verify tenant information is properly set in the request context
- Authentication failures: Check that authentication headers and parameters are correctly configured
- Network connectivity: Verify that the Transparent Proxy is accessible from your environment
Evaluating Transparent Proxy Headers
When using proxy servers it can be difficult to troubleshoot issues as it is often not obvious where exactly the error occurred.
To make troubleshooting easier the Transparent Proxy adds additional response headers to provide more information about where an error occurred. For the above example of executing OData requests you can access the response headers as follows:
try {
// execute OData request
} catch (ODataResponseException e) {
System.out.println(e.getHttpCode());
// the Transparent Proxy will attach additional response headers in case an error occurred
System.out.println(e.getHttpHeaders());
}
List of headers added by the Transparent Proxy
X-Error-Origin- the source of the errorX-Proxy-Server- the proxy server (Transparent Proxy)X-Error-Message- thorough error messageX-Error-Internal-Code- set only when the source of the error is the XSUAA or Destination service. The value is the HTTP code returned from one of these services.X-Request-Idis sent with the response in all requests, both successful and failed
For more information, see Troubleshooting
Related Documentation
- HTTP Client - For using destinations with HTTP clients
- Kubernetes Connectivity
- HTTP Destinations
- On-Premise Connectivity
- SAP BTP Destination Service
For more information about the Transparent Proxy itself, refer to the official SAP documentation.