Spring Boot custom SSL key-store loading logic - java

I'm using Spring boot 2.x and I need to configure the SSL on it.
To enable the SSL it's easy: just filling these properties:
server.port=443
server.ssl.keyStoreType=PKCS12
server.ssl.key-store=classpath:keystore/mycertificate.p12
server.ssl.key-store-password=mypassword
security.require-ssl=true
But I need this additional behaviour: Instead of loading the certificate from a specific path (server.ssl.key-store) I need to have a custom loading logic: I need to implement this custom way using, for example, the AWS Secrets service or similar.
So, is it possible? Is there a possibility to extend this default behaviour in Spring specifying a custom loader or something like this?

spring boot uses by default tomcat and tomcat can only be configured with the properties which you mentioned in your question. So it is not flexible and I think it is not possible to accomplish this with the default spring boot configuration with tomcat. I faced similar challenges and the solution I applied was to replace the embedded tomcat server with jetty. With Jetty it is possible to configure the ssl properties on runtime while getting it from for example your aws secrets service. See this project as an example: https://github.com/Hakky54/java-tutorials/tree/main/instant-server-ssl-reloading/server
So what I can recommend is to first exclude tomcat from your dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
Include Jetty server to spring boot:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
#Bean
public SslContextFactory.Server sslContextFactory() {
return fooService.createSslContextFactory();
}
#Bean
public ConfigurableServletWebServerFactory webServerFactory(SslContextFactory.Server sslContextFactory) {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
JettyServerCustomizer jettyServerCustomizer = server -> {
ServerConnector serverConnector = new ServerConnector(server, sslContextFactory);
serverConnector.setPort(8443);
server.setConnectors(new Connector[]{serverConnector});
};
factory.setServerCustomizers(Collections.singletonList(jettyServerCustomizer));
return factory;
}

Related

Spring OAuth2 Authorization Server + Spring WebFlux

I'm trying to rewrite existing OAuth2 authorization service using Spring Boot 3.0.2 and newly released Spring OAuth2 Authorization Server 1.0.0.
Faced a trouble combing objects from Reactive Security and Standard Security libraries: unable to apply default security to OAuth2AuthorizationServerConfiguration class, because it's not applicable to reactive ServerHttpSecurity.
Code part
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityWebFilterChain authServerSecurityFilterChain(ServerHttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http
.formLogin()
...;
return http.build();
}
Can't pass HttpSecurity to applyDefaultSecurity() method.
Tried to find any reactive implementations of OAuth2AuthorizationServerConfiguration class but found nothing.
Is there any way to convert ServerHttpSecurity to HttpSecurity? Or Spring OAuth2 Authorization Server is completely incompatible with reactive approach?
Main dependencies of Maven pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>1.0.0</version>
</dependency>
Thanks is advance.
UPD
Searched badly. Seems like it's not supported yet:
https://github.com/spring-projects/spring-authorization-server/issues/152
Or there are still some ways make it work?
Official answer:
We are strictly focusing on a Servlet implementation for the initial set of features that would qualify for a MVP version. We haven't decided whether we'll provide a WebFlux version at this point.
Quite honestly, I'm not convinced it's needed. The client and resource server(s) are the most active, whereas, the authorization server is not as active as it simply issues a token and may validate a token, which is limited activity between the many interactions that follow between a client and resource server after a token is issued.
Either way, I'm going to close this issue as WebFlux is not on the roadmap as of now.
Source - https://github.com/spring-projects/spring-authorization-server/issues/152

Spring actuator endpoints does not work on the application port

I am trying to enable the actuator endpoints on the same port as the application port (specified in the application.properties file by the server.port=8080) but for some reason, it does not work. When I run the application, I can get back the response from the application but not from the actuator endpoints. I can see the logs mention the endpoints being exposed beneath base path '/actuator' as shown in the screenshot below. But when I try to hit the actuator URL, it gives a 404.
URL, not working:
http://localhost:8080/actuator
http://localhost:8080/actuator/health
http://localhost:8080/actuator/info
However, if I specify a separate port in application.properties for the actuator endpoints with the property (management.server.port=9000) then it works fine.
URL, that's working:
http://localhost:9000/actuator
http://localhost:9000/actuator/health
http://localhost:9000/actuator/info
The only difference is about the port number but from what I read in the spring documentation, the actuator endpoints should by default be enabled on the application port if we don't specify the management.server.port.
Can someone please explain what am I missing here?
PS: The application run logs are exactly the same with or without specifying the management.server.port, hence, this one screenshot is without specifying the management port.
Also, I tried giving the same port number for both the property (server.port and management.server.port) but the same problem occurs. The application works on that port but the actuator endpoints do not.
I am using the spring-boot version 2.0.6
These are the contents of my application.properties file:
camel.springboot.main-run-controller=true
camel.springboot.name=AppName
camel.rest.data-format-property.prettyPrint=false
camel.component.servlet.mapping.context-path=/*
server.port=8080
management.server.port=9000
management.endpoint.health.show-details=always
management.endpoint.beans.enabled=true
logging.level.org.springframework = INFO
logging.level.org.apache.camel.spring.boot = INFO
logging.level.org.apache.camel.impl = DEBUG
Here are the dependencies in pom.xml:
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-servlet-starter</artifactId>
</dependency>
</dependencies>
Regarding the Spring Boot Actuator documentation
Exposing management endpoints by using the default HTTP port is a
sensible choice for cloud-based deployments. If, however, your
application runs inside your own data center, you may prefer to expose
endpoints by using a different HTTP port.
it serves the Actuator using the default HTTP port (which is 8080). I did a quick check and could confirm this with Spring Boot 2.1.X and 2.2.X.
Try to remove the management.port from your config and if this does not work then the problem might come from an additional (custom) configuration in your application.
Remove management.server.port=9000, this property overrides server.port=8080.
Please set the below in application.properties file
management.endpoints.enabled-by-default=true
management.endpoints.web.exposure.include=*
I had the same problem, what was causing this for me was a conflict between the context I declared for camel and the one being used for spring actuator.
So, at application.yml I had these 3 contexts:
server: servlet: context-path: myservercontext/v1
camel: component: servlet: mapping: contextPath: /*
management: endpoints: web: base-path: "/"
I think after the request hits the server it gets lost on this ambiguity so i solved it by changing these contexts to:
server: servlet: context-path: myservercontext/v1
camel: component: servlet: mapping: contextPath: /camel/*
management: endpoints: web: base-path: "/"
This worked out nicely for me since all my resources where grouped under "/camel" anyway but as I tested I found out this also works:
server: servlet: context-path: myservercontext
camel: component: servlet: mapping: contextPath: /v1/*
management: endpoints: web: base-path: "/"
Just note that in this last scenario the health route won't be under "v1".
I would never have figured out the cause of this problem if it wasn't for this question, even if it wasn't solved yet, so I thank you a lot for posting this and hope my solution will manage to help others ^^

How to get metrics of outgoing http calls in Spring Boot?

My Spring Boot application calls other providers and I'd like to measure the time a response takes, but also would nice to measure other stuff. Is there a clean way of doing this maybe some library or package?
I'd need something which integrates seamlessly with existing apps, and I do not need to wrap the calls with System.getCurrentTimeMillis() etc.
You can use Spring Actuator,Prometheus Server/Client and Grafana Server to monitoring your application. There are 4 types of metrics available in Prometheus, you can use according to your requirements.
Prometheus Documentation
https://prometheus.io/
Grafana Documentation
http://docs.grafana.org/
Install Prometheus and Grafana servers.
You have to add the dependencies for Prometheus Client. Also Spring Actuator dependency need to add.
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_spring_boot</artifactId>
<version>0.0.26</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId>
<version>0.0.26</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_servlet</artifactId>
<version>0.0.26</version>
</dependency>
In Configuration file you have to define bean for metrics.
#Bean
public ServletRegistrationBean servletRegistrationBean() {
DefaultExports.initialize();
return new ServletRegistrationBean(new MetricsServlet(), "/prometheus");
}
You can follow https://g00glen00b.be/monitoring-spring-prometheus-grafana/ for more detail.
Prometheus can monitor some metrics in the jvm.
But you may want to see the javamelody spring-boot-starter which will give metrics on the jvm and on the application and also on #Service Spring components and on RestTemplate when defined as bean to measure calls to some other providers.

How to activate JMX monitoring in spring boot standalone app

I went via almost all docs and all but not able to get grip on this mysterious stuff.
so my question - Can I use my standalone spring boot app to monitor health and other metrics of my app via http jmx url? Do I need to configure something else for this?
I have added below dependency in boot app.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
I have also configured below properties in my config file.
management.endpoints.web.exposure.include=*
management.endpoints.jmx.unique-names=true
management.server.port=8080
management.server.ssl.enabled=false
When I try to hit URL : http://localhost:8080/actuator/jolokia/health I am not able to see any results.
Also tried adding custom end point like below but not working.
#Endpoint(id="mypoint")
#Component
public class myPointEndPoint {
#ReadOperation
public String mypoint(){
return "Hello" ;
}
with additional property
management.endpoint.mypoint.enabled=true
The problem is the url you are trying to invoke.
First, retrieve the possible mbeans with: http://localhost:8080/actuator/jolokia/list
When you take a look at the Health mbean, you must provide the unique name and the operation (op).
In my case, it looked like: http://localhost:8080/actuator/jolokia/exec/org.springframework.boot:type=Endpoint,name=Health,identity=4200098/health
Also check the Jolokia documentation: https://jolokia.org/reference/html/index.html

Apply /refresh on multiple instances annotated with #refreshScope

I'm writing spring boot application, which using spring configuration, deployed on pivotal cloud foundry and exposed by Netflix Eureka as discovery serivce / load balancer.
I have created a bean as followed:
#Component
#ConfigurationProperties("config")
#RefreshScope
#Data
public class GeneralProperties {
private boolean ignoreEvent;
}
When calling to the application route that Eureka exposed with /refresh after changing the actual property in the configuration repository, the value that annotated by #refreshScope was changed (end in the response status the field exsiting), which means it's working correctly.
The issue starts when running multiple instances of the same application on the cloud, and calling to the /refresh.
The route that beeing used is the one that exposed by Eureka, which using it's load balancer to route the call to one of the available instances.
It leads to unexpected results that not all the instances are getting updated with the latest change in the property.
Any suggestions how to apply the change on all instances?
You should use Spring Cloud Bus in such a case.
The idea behind this framework is to bind all your application instances to a topic in a message broker (RabbitMQ or Apache Kafka).
Add the following dependency to your pom.xml:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus-parent</artifactId>
<version>1.3.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
</dependencies>
In the above example I added a dependency on amqp which is RabbitMQ. You'll also need to bind your application to the RabbitMQ, in PCF it's easy since it's built in to the platform.
When you need to refresh, you should invoke:
POST /bus/refresh
This would trigger an event to a topic that all instances of your application are listening to, and as a result - all instances would refresh their bean configuration.
Good luck.

Categories

Resources