Spring Boot OpenFeign: Different SSL settings per auto-generated Client - spring

I am trying to setup my #FeignClient with SSL settings.
I can easily setup a #Bean for Feign.Builder which gives a Client with a SslSocketFactory. No problem.
But I have multiple different #FeignClient interfaces, and I want different SSL settings per endpoint.
The #FeignClients I am using is created by a code-generator, so I cannot use the annotation configuration= property to point to a special #Configuration for certain #FeignClient.
I have tried looking into injecting custom FeignClientSpecification, but I cannot get it to work properly.
Ideas?
(I could just not use the auto-generated interfaces and build my own, but feels less pretty)

Related

How to best configure Spring Kafka with SSL through Java Beans (rather than from the application.yml)?

I have a Java (11) Spring Boot microservice using the Spring Kafka starter (together with an Axual wrapper, but this is probably not relevant). My business requires authenticating to the cluster using SSL. I have set up things locally and it's all working neatly.
But now I am tasked with organising the project and the configuration in a secure and proper way, which primarily includes separating the SSL keystore, truststore and the passwords from the source code repo. In my local setup I have them in my resources directory as .jks files.
The current setup for storing secrets is using a Spring Config Server that we connect to using Spring cloud. This SCS has a Credhub instance that can be used to store secrets. in another project that uses SSL for an mTLS connection with a third party API, I have stored the certificates in base64 in this credhub, and then load them into a bean when the app boots up. Here I use the Java.net.ssl SSLContext class and the java.security Keystore class to build a keystore and a truststore in code, then inject the SSLContext bean into a RestTemplate bean and without going into the specifics, voila it is configured.
I was hoping to do something similar in Spring kafka. But whereas the Spring RestTemplate can be constructed using a bunch of factory classes that at their base take in this SSLContext object, I struggle to find the way the ConsumerFactory can be configured except by supplying a property in the KafkaProperties that points to an already made .JKS file.
I was looking to build up my SSLContext or simply my KeyStore object in Java, inject it as a Spring bean and use it to configure the factory. Now, the KeyStore class has a 'store(.., ..)' method that can be used to create a .jks file - but this seems a roundabout way of doing it. It just seems a bit hacky to do it that way.
The way I see it, I have a few options.
A: find a way to inject the SSL config as a bean and configure the factory in code
B: load the SSL config in java in code, then save it as a file for the Spring Kafka to load normally
C: Inject the .jks file in the pipeline from a secure location into the container
D: inject the .jks file into the container on boot-up through another kind of secret storage solution
I prefer option A because I like working the framework and deepening my understanding.
Option B seems, like I said, hacky. But perhaps someone here will tell me it's totally fine. Then I would like to know how best to do this. Do I simply call the .store() in my ConsumerFactory bean before I return it? Can I extend or configure the SpringApplication.run() to maybe execute this creating and storing of the keystore file before loading all the other beans? Any ideas would be welcome.
Option C and D will solve my issue. They may even be the better solutions. But for my own understanding of the framework, if you can help me figure out A (or possibly B) I would love to hear it. If then the conclusion is still "go for C you're better off that way" then at least I know the ins and outs of it.
It's not entirely clear what you mean, but the factory (producer and consumer) has updateConfigs().
/**
* Update the consumer configuration map; useful for situations such as
* credential rotation.
* #param updates the configuration properties to update.
* #since 2.7
*/
default void updateConfigs(Map<String, Object> updates) {
}
For option A, you can inject Boot's auto configured factories into another #Bean definition and mutate the configs there.
I don't think the kafka-clients code provides a mechanism to inject your own SSLContext, but I could be wrong.

What is the correct way to add custom Prometheus metrics to a client library?

We have recently started monitoring our springboot apps with the micrometer prometheus integration. For the most part it works great out of the box; however, we have a client lib which wraps an http client for communicating with one of our apis that does NOT use Spring components, and is therefore excluded from the http_client_requests_* metric set.
The solution is obviously a #Timer annotation to manually add those custom metrics, and since our client api lib only exposes two methods, it would be ideal to just annotate these methods instead of having to add a timer anywhere I make a call with the lib in my app. The problem is, all of the setup instructions I find assume you're adding the integration to a server app instead of a lib, and I'm having trouble getting it to work.
I first tried adding the actuator and micrometer prometheus dependencies using compileOnly and runtimeOnly to hopefully not collide with the same dependencies being imported into my app, but even when that doesnt work and I change the imports to 'implementation' I get the 'No bean found with type of MeterRegistry' error other people have reported when I try to expose the TimedAspect bean like so:
#Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
Even if I got this to work, my thinking is I'd run into issues again with the
management.endpoints.web.exposure.include=health,info,prometheus
configuration the tutorials say is necessary to enable metrics collection, since my app already does this. So, before I get much deeper in attempting this approach I'd like to ask: Is there a better way to accomplish what I'm trying to do?

Spring Boot: Extend/Edit Apache HttpClientBuilder with different configurations?

I am having an architectural issue with the Apache HttpComponents HttpClient.
We have a system where we have several different remote endpoints that we want to contact, and each have some different configurations like ssl, basic auth, et cetera.
I am using Spring Boot and Cloud Sleuth from which I get a HttpClientBuilder that gives me tracing and other things. I want to re-use that HttpClientBuilder but on-top of that add my own specific configurations, for each unique endpoint.
The problem though is that the HttpCientBuilder is not immutable with withXYZ() methods, nor is there a copy or clone method on the builder, so I can't copy the original and change just my specific changes there without altering the base HttpClientBuilder and get into conflicts with others that use the same instance of the builder. Be it racing conditions between threads or conflicting configurations between the different endpoints.
One place in the Spring Boot project where I have seen them seemingly wanting to do something similar is in HttpClientConfiguration of Spring Cloud Commons where it creates an own ApacheHttpClientFactory which takes the original autowired HttpClientBuilder and then sets disableContentCompression(), disableCookieManagement() and useSystemProperties() -- but it seemingly does it to the original instance of the HttpClientBuilder which just seems completely wrong to me. It will alter how all the built HttpClient works, and not just the one they will later be using in their Ribbon code in HttpClientRibbonConfiguration of Spring Cloud Netflix Ribbon. A potential bug in hiding? To me it seems like it, since it highly depends on calling order.
Does anyone have any ideas how something like this should be solved?
The easy alternative that I could do is to just not try and build upon the given HttpClientBuilder from Sleuth, but instead build a completely new one from the ground-up every time I need one, and check if a HttpTracing bean is available and use TracingHttpCientBuilder instead of HttpClientBuilder in that case, but this seems counter-intuitive.
I ran into the exact same problem. However, I had the option of defining my own HttpClientBuilder bean. I am not sure you are in the same position?
But by making the bean scope "prototype" a new bean instance is created every time it is injected somewhere. Then I could safely modify the HttpClientBuilder after injection.
#Bean
#Scope("prototype")
public HttpClientBuilder tracingHttpClientBuilder(..) {
...
}

How to require SSL in some environments using Spring Boot 2.0.0.M4+

While upgrading my Spring Boot applications through the 2.0.0 milestone releases, I've noticed that starting in 2.0.0.M4 security.require-ssl and other security configuration options are gone. I didn't find any mention of deprecations or a new approach in the current docs so I dug around and found the GitHub issue where the work originated. I applaud the goal in the GitHub issue to:
significantly simplify security configuration in 2.0.
and am happy change my patterns to upgrade, but I'm a little stuck on how to require SSL in specific environments. I know I can accomplish a similar outcome in my WebSecurityConfigurerAdapter configuration using http.requiresChannel().anyRequest().requiresSecure() but I don't want to enable this setting in every environment I run my applications in (e.g. pre-production environments without a certificate, local development on localhost). I know I could put some conditional logic in my WebSecurityConfigurerAdapter configuration but I like to keep my Spring configurations "environment agnostic" and keep my environment specific configurations in properties files specific to a profile.
After these recent simplifying changes to Spring Boot's security configuration, what's a recommended approach to require SSL in some environments?
There are a few options depending on your setup.
Changing the RequestMatcher
If the only time you require HTTPS is in an environment with a custom header injected by a Proxy (i.e. X-Forwarded-* headers), you can do something like this:
http
.requiresChannel()
.requestMatchers( r -> r.getHeader("X-Forwarded-Proto") != null).requiresSecure()
.and()
// ...
The nice thing about this approach is that you do not need to do anything to turn HTTPS on or off. The downside is that there is a very minimal performance hit of comparing the headers. This performance hit should be negligible, but may not be aesthetically pleasing to some.
Conditional Configuration
Since this is Java Code you can always have a boolean flag that determines if you require HTTPS or not. For example, create a member variable and resolve it to Externalized Configuration with something like this:
#Value("${security.require_ssl}")
private boolean requireHttps;
Then use something like this:
if(this.requireHttps) {
http
.requiresChannel()
.anyRequest().requiresSecure();
}
http
// ...
This approach aligns nicely with Spring Boot 1.x approach and it does not suffer from the additional if statement in each request. However, it requires a property to be managed.
Profiles
As mentioned by Madhura you can also use #Profile. I find this approach is better suited if you have lots of differences between environments and not as ideal for something minor like determining if HTTPS is required. The problem with this approach when making a small change is you run into a lot of duplication.
You're absolutely right about adding your own WebSecurityConfigurerAdapter for customizing security configuration. To keep that configuration environment agnostic, you can add an #Profile so that it can be switched on and off depending on the active profile. If it is only the ssl configuration that is environment agnostic, and the rest of the security configuration is the same for all environments, you can add a WebSecurityConfigurerAdapter that only configures http.requiresChannel().anyRequest().requiresSecure() that has the #Profile on it and order it such that it is the first one that kicks in.

Generate two different SP metadata using Spring Security SAML´

We need to configure two different IdPs. So far so good, but the problem is that we need to configure forceAuthN for one of the IdPs. Another thing is that one IdP is using HTTP-Artifact for bindingsSSO, while the other is using HTTP-POST. Therefore it seems that we need two different SP metadata.
What is the best approach? I guess it's not a problem if we write the metadata ourselves, but we want Spring Security SAML to generate these.
We tried to configure two different metadataGeneratorFilters by extending the MetadataGeneratorFilter class. The problem now is that only metadata for one of the SPs are built, and the reason is simply the following line in the processMetadataInitialization method:
if (manager.getHostedSPName() == null) { ...
The MetadataManager class can, as far as I know, only hold one hostedSPName, not two.
You don't need separate metadata in order to control selection of bindings or forceAuthN. These settings are configured in the AuthnRequest sent to IDP. Just make sure that your SP metadata contains both HTTP-Artifact and HTTP-POST endpoints (which it does by default).
The AuthnRequest settings are configured using instances of WebSSOProfileOptions. Extend class SAMLEntryPoint, override method getProfileOptions and return correctly configured WebSSOProfileOptions depending on the IDP you are connecting to and the requirements you have. Details are in the manual.
The forceAuthN in AuthnRequest is configured using property forceAuthn. You can select which binding (artifacts vs. post) should IDP use to deliver the SAML message back to SP with property assertionConsumerIndex.
You cannot use automatic MetadataGenerator for generating of two different sets of metadata in one instance. In order to do so you can use pre-configured metadata - but I don't think it's needed in your case.

Resources