Spring Gateway - multiple httpclients in Spring Gateway? - spring

I have many filters that I use to manipulate different requests.
I'm overriding the default netty httpClient provided by Spring Gateway so I can set programatically some sslContext - mTLS in my case. This is fine, but I will also need to have a second netty httpClient so I can connect to other server with no ssl at all, or just regular tls.
I was wondering if it's possible either to set sslContext on specific filters or to decide what httpClient to use based on the filter or some other extension point that spring gateway provides.
Do you think it is possible to have multiple httpClients and decide which one to use?
I tried to create a new bean which configures a httpClient and set some sslContext like this:
#Bean
public reactor.netty.http.client.HttpClient httpClient() {
final PrivateKey privateKey = getPrivateKey();
final X509Certificate x509Certificate = getX509Certificate();
final SslContext sslContext =
SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.keyManager(privateKey, x509Certificate)
.build();
return reactor.netty.http.client.HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
}
While I use this httpClient to connect via mTLS it works fine, but I'd like to decide what httpClient to use based on specific filters.

Related

How can I authenticate a Ribbon load balancer and Zuul proxy using a certificate?

I have a Spring application, that acts as an authentication proxy for two backend servers. A user will access the Spring application and be forwarded to the backend once he is successfully authenticated. To prevent unwanted access without prior authentication the backend servers require a certificate as authentication.
My Spring application uses Netflix-Ribbon as a load balancer and Netflix-Zuul as a Proxy for the users requests. How can I configure them to use the client certificate that is required for the authentication on the backend servers?
Ok, I figured it out. You can configure your own CloasableHttpClient as a #Bean and create a custom SSL context. You can provide a certificate to a server through .loadKeyMaterial(). Zuul will then use these settings.
#Configuration
public class HttpClientConfig {
#Bean
public CloseableHttpClient httpClient() throws Throwable {
String keyPassphrase = "yourCertificatePassword";
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("Path/to/your/clientCert.pfx"), keyPassphrase.toCharArray());
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, keyPassphrase.toCharArray())
.build();
return HttpClients.custom()
.setSSLContext(sslContext)
.build();
}
}

Does calling https service by Resttemplate work for all services whose certificate has been imported in the trustore of my client service?

I have a restemplate which can make call to multiple external systems over https.
I configured the resttemplate like so :
SSLContext sslContext = SSLContextBuilder
.create()
.loadTrustMaterial(key, keyPassword)
.build();
HttpClient client = HttpClients
.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
client);
httpComponentsClientHttpRequestFactory.setConnectTimeout(5000);
httpComponentsClientHttpRequestFactory.setReadTimeout(30000);
return new RestTemplate(httpComponentsClientHttpRequestFactory);
I have setup 2 mock services on 2 separate VMs with ssl enabled and I am testing this restemplate by calling those services over https. And it works.
What I want to confirm is that configuring the restemplate as shown in the above code and importing the certificates of the different services to be called in by the client truststore should work without any additional configurations right?
I am confused because in the code in this example here : https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose the author has used 2 different factories i.e. serverTomClientHttpRequestFactory & serverAliceClientHttpRequestFactory, however mine works with a single restemplate. can someone please shed some light on this topic?

How to specificy preconfigured webshpere-liberty SSLSocketFactory to Netty rective client used by spring-webclient

I am building a spring boot application that runs on #websphere-liberty profile. I am using a Spring webclient for outbound connections. All good except that I am not able to specify liberty SSL context to the netty httpclient used by Spring Webclient.
Any help on how how to specify pre-configured libery ssl context to Netty httpclient is greatly appreciated.
IBM documentation says that I should use SSLSocketFactory.getDefault() to get liberty SSL context. But I don't see any way to pass this to Netty HttpClient.
Ref: [https://www.ibm.com/support/knowledgecenter/en/SSEQTP_liberty/com.ibm.websphere.wlp.doc/ae/twlp_sec_ssl.html]
This is what I tried, but it is not pointing to the SSL context configured by #Websphere-liberty. Instead, it seems to create a new one. Because of that, the trust/keystores are not available to the netty HttpClient and I get an error saying that the peer is not trusted.
#Bean
public WebClient createWebClient() throws SSLException {
JdkSslContext jdkSslContext = new
JdkSslContext(SSLContext.getInstance("SSL"),true,
null, IdentityCipherSuiteFilter.INSTANCE,null,ClientAuth.NONE,
null,false);
ClientHttpConnector httpConnector = HttpClient.create().secure { t -> t.sslContext(jdkSslContext) }
return WebClient.builder().clientConnector(httpConnector).build();
}

How should I use the Spring WebClient to non-interactively access an OAuth protected resource on behalf of another user?

I have a Spring (not Boot) application which has to access non-interactively (in a scheduled task) some 3rd-party resources on behalf of our users. These resources use OAuth 2.0 for authorization. We already have a workflow that gets us the required tokens and are accessing the resources using either Spring Social or our own implementation neither of which is optimal (Spring Social seems to be not maintained, we'd rather use a library than maintain our OAuth "framework").
I'm trying to use the WebClient from Spring Security 5.1, but I'm not sure I'm using it correctly.
The WebClient is created this way:
final ClientRegistration 3rdParty = 3rdParty();
final ReactiveClientRegistrationRepository clientRegistrationRepository =
new InMemoryReactiveClientRegistrationRepository(3rdParty);
final ReactiveOAuth2AuthorizedClientService authorizedClientService =
new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
final ServerOAuth2AuthorizedClientRepository authorizedClientRepository =
new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
final ServerOAuth2AuthorizedClientExchangeFilterFunction autorizedClientExchangeFilterFunction =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository);
return WebClient.builder()
.filter(autorizedClientExchangeFilterFunction)
.build();
and accessing the resource this way works:
final OAuth2AuthorizedClient oAuth2AuthorizedClient = ... // (OAuth2AuthorizedClient with OAuth2AccessToken)
final Mono<SomeResource> someResourceMono = webClient().get()
.uri(3rdpartyUrl)
.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(oAuth2AuthorizedClient))
.retrieve()
.bodyToMono(SomeResource.class);
The problem is I don't see how the ReactiveClientRegistrationRepository and ServerOAuth2AuthorizedClientRepository are used in this approach. If I have to create a fully populated OAuth2AuthorizedClient to access the resource, why are these repositories needed?
I expected, that I have to pass the clientRegistrationId, some "principalName", implement our ReactiveOAuth2AuthorizedClientService loading OAuth2AuthorizedClient's by "principalName" and let the ServerOAuth2AuthorizedClientRepository do its work, but the only way I see to pass a principal to the WebClient is by using ServerOAuth2AuthorizedClientExchangeFilterFunction#oauth2AuthorizedClient which requires a complete OAuth2AuthorizedClient. Which is the part I'm doing it wrong?
Instead of supplying the OAuth2AuthorizedClient via oauth2AuthorizedClient(), you can also provide the clientRegistrationId via clientRegistrationId() and ServerWebExchange via serverWebExchange(). The combination of the latter 2 options will resolve the Principal from the ServerWebExchange and use both ReactiveClientRegistrationRepository and ServerOAuth2AuthorizedClientRepository to resolve the OAuth2AuthorizedClient. I understand your use-case is a bit different given you are running outside of a request context - this is just a FYI.
...The problem is I don't see how the
ReactiveClientRegistrationRepository and
ServerOAuth2AuthorizedClientRepository are used in this approach
You still need to provide ReactiveClientRegistrationRepository and ServerOAuth2AuthorizedClientRepository as the ServerOAuth2AuthorizedClientExchangeFilterFunction supports the refreshing (authorization_code client) and renewing (client_credentials client) of an expired access token.
For your specific use case, take a look at UnAuthenticatedServerOAuth2AuthorizedClientRepository as this implementation supports WebClient running outside of a request context, e.g. background thread. Here is a sample for your reference.

Spring RestTemplate call to HTTPS *without* HttpClient

Trying to make REST calls with RestTemplate using the server cert as the client cert to make calls with.
Don't ask me why :-), but I don't want to use Apache's HttpClient. I just think it's overkill.
I've seen code that uses regular JDK's SSLContext to set things up system-wide, i.e., set up SSLContext and the call SSLContext.setDefault(sslContext) as in this code:
// ... keymanagers, trustmanagers are omitted here
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keymanagers, trustmanagers, new SecureRandom());
SSLContext.setDefault(sslContext);
However, I saw a GitHub comment here (https://github.com/spring-projects/spring-boot/issues/6493) that states that setting SSLContext as default like that isn't really a good idea.
So, my question is: outside of having to use HttpClient, is there a way to use the sslContext configured in the code snippet above in RestTemplate setup somehow?
You can override the default requestFactory in the restTemplate by doing
restTemplate.setRequestFactory(new MyCustomRequestFactory());
Have a look at org.springframework.http.client.SkipSslVerificationHttpRequestFactory here for an example of setting SSLContext in your request factory.

Resources