There's a difference in implementation between two versions of Spring Boot Actuator (1.2.5 and 1.3.0) in HealthMvcEndpoint, isUnrestricted() method ( && and || ). I understand that this is to preserve these restrictions
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#production-ready-health-access-restrictions
But is there any solution now to enable only one endpoint (e.g. Health) unrestricted with full content, without exposing all the others?
Disabling management.security.enabled is just making all the endpoints accessible without authentication (?) - it doesn't look like it's taking endpoint sensitivity with it.
I managed to partially solve this by making all the endpoints disabled in the first place by endpoints.enabled = false with disabling their security management.security.enabled = false
and enabled the ones I wanted without security - like the Health endpoint
endpoints.health.enabled = true and endpoints.health.sensitive = false.
With actuator 2 these properties have been changed
To disable all the actuator endpoints use
management.endpoints.enabled-by-default=false
To enable specific endpoint use management.endpoint.<id>.enabled property. The following example enables the shutdown endpoint:
management.endpoint.shutdown.enabled=true
Official Documentation
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html
Related
I am trying to secure some of my spring cloud gateway routes:
Users must be authenticated using OAUTH2 to be able to use those routes (if not -> respond with http 401)
The JWT access token must include a specific value in the "scp" claim ("2fa" in my case) (if not, respond with http 403)
The JSON payload contains one property "user" that must have the same value as the "sub" claim in the JWT access token. (if not, respond with http 403)
Reading the documentation I found out how I can set up 1. and 2.
Unfortunately, there seems to be very little information on how to achieve 3.
Where I could I find a working example?
Here's my spring security setup from application.yaml file:
...
spring:
profiles: production
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${AUTH_URL}/oidc
jwk-set-uri: ${AUTH_URL}/oidc/jwks.json
...
Configuation of my SecurityWebFilterChain:
...
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE - 3)
public SecurityWebFilterChain secondFactorScopeApiHttpSecurity(ServerHttpSecurity http) {
final ServerWebExchangeMatcher baseScopeEndpointsMatcher = new OrServerWebExchangeMatcher(
new PathPatternParserServerWebExchangeMatcher("/api/fhir"),
new PathPatternParserServerWebExchangeMatcher("/api/fhir/List**"),
new PathPatternParserServerWebExchangeMatcher("/api/fhir/Observation**")
);
http.securityMatcher(baseScopeEndpointsMatcher)
.authorizeExchange(exchanges -> exchanges.anyExchange().hasAuthority("SCOPE_2fa"))
.oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt);
return http.build();
}
...
I want the user to see a HTTP 403 in case the payloads "user" property does not match the sub claim from the JWT.
What you want to achieve is an easy task on resource-servers and, in my opinion, resources access-control is the responsability of resource-server, not gateway, specially if access decision involves the resource itself.
I would just let the gateway be transparent to OAuth2: leave requests authorization header as well as responses status code unchanged.
I have samples in that series of tutorials which incrementally builds to advanced role based access control. It should take you less than an hour to follow the first 3:
1st demoes resource-server security conf with spring-boot-starter-oauth2-resource-server (what you've implemented on the gateway so far)
2nd shows how to replace JwtAuthenticationToken with an implementation of your choice exposing strongly typed private-claims. It also greatly reduce Java conf with one of the thin wrappers I created around spring-boot one.
3rd demoes security SpEL customization to write stuff like
#GetMapping("/on-behalf-of/{username}")
#PreAuthorize("is(#username) or isNice() or onBehalfOf(#username).can('greet')")
public String getGreetingFor(#PathVariable("username") String username) {
...
}
Of course, in your case, you would use a signature like myControllerMethod(#RequestBody MyDto dto, Authentication auth) and an expression like #dto.sub eq #auth.name, but you get the idea.
I'm using Spring Cloud API Gateway, the app has yaml based configuration and it works well since all routes are internal links, means there is no need for any proxy.
But, there a dependency on Spring Security which is used to validate JWT token and the URL of issuer is the resource that can be reached only via proxy.
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: https://some-site.com/itmp/v1/.well-known/jwks.json
issuer-uri: https://some-site.com/itmp/v1/
And here is the problem: I can not force WebClient using the http.proxyHost parameter. I tried many techniques that I found all around the Internet like setting them implicitly in app properties file, or hard coded in application itself. But nothing worked out for me. Maybe I'm missing something, maybe Spring Cloud has some special tweaks for that behavior?
FYI Project is based on Spring Boot 2.6.6 (reactor-netty 1.0.17 which is normally supports proxyWithSystemProperties )
I recently had this exact problem myself. After a lot of back and forth I ended up creating my own ReactiveJwtDecoder bean and setting the HttpClient/WebClient it uses internally (using NimbusReactiveJwtDecoder.webClient(WebClient)). Something along the lines of:
#Bean
ReactiveJwtDecoder jwtDecoder(#Value("${ISSUER_URI}") String issuerUri,
#Value("${JWKS_URI}") String jwksUri) {
HttpClient httpClient = HttpClient.create().proxyWithSystemProperties();
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
var jwtDecoder = NimbusReactiveJwtDecoder
.withJwkSetUri(jwksUri)
.webClient(webClient)
.build();
// import org.springframework.security.oauth2.jwt.JwtValidators;
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuerUri));
return jwtDecoder;
}
You can probably inject the OAuth2ResourceServerProperties-bean instead of those two #Value-annotated Strings to get the URLs.
Also, be aware that if you application already does more customized JWT claim validation than merely checking iss, exp and nbf, you must remember to inject the existing JwtValidator and use that instead of the JwtValidators.createDefaultWithIssuer(String) one.
We are using Spring Boot in 2.4.2 with Spring WebFlux.
I want the Spring Boot application to terminate all requests to the application that take longer than say 3 seconds to process.
There is server.netty.connection-timeout, but that doesn't seem to do the trick.
Is there a way to specify such a server request timeout?
I was also facing the same issue i.e. even after configuring server.netty.connection-timeout request would get canceled. So, after some debugging found that timeout was getting set to '30000' by AsyncContext.
So, I configured the following property spring.mvc.async.request-timeout which change the timeout being set in AsyncContext and the request stopped getting canceled.
TL;DR:
Netty has no request timeout*. Add this WebFilter to set a request-timeout of 3 seconds using the reactor timeout on every request (here in kotlin, but in Java it works accordingly):
#Component
class RequestTimeoutWebFilter : WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
return chain
.filter(exchange)
.timeout(Duration.ofSeconds(3))
}
}
* at least I could not find any information in the netty docs.
Detailed answer
The connection-timeout does not refer to the duration that a request is allowed to take for processing, but it refers to the time it takes for establishing the connection.
First, I could not find any spring configuration option that allows setting the request timeout for netty. Then I went through the netty documentation to find out that there is no concept of request timeouts on the http server (only on the http client).
Wondering about why such important feature would not be supported, I remembered that we often cannot apply the same concepts as in blocking servers for good reasons. Next, I remembered, that in the reactive world we do not directly implement the handling of the request, but how the handling is assembled - i.e. we hold a Mono<Void> that will handle the request. Hence, we can just look at reactor and how to timeout a Mono, which is very easy:
Mono.create(...)
.timeout(Duration.ofSeconds(3))
Next, we just need to figure out, how to apply this to all requests. This is easy as well, because we can just use a WebFilter to intercept all requests to apply our timeout (here in kotlin, but in Java it works accoringly):
#Component
class RequestTimeoutWebFilter : WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
return chain
.filter(exchange)
.timeout(Duration.ofSeconds(3))
}
}
This effectively cancels a request within the set timeout with the following error:
2022-10-21 00:08:00.981 ERROR 6289 --- [ parallel-4] a.w.r.e.AbstractErrorWebExceptionHandler : [59dfa990-7] 500 Server Error for HTTP GET "/some/route"
java.util.concurrent.TimeoutException: Did not observe any item or terminal signal within 3000ms in 'source(MonoDefer)' (and no fallback has been configured)
at reactor.core.publisher.FluxTimeout$TimeoutMainSubscriber.handleTimeout(FluxTimeout.java:295) ~[reactor-core-3.4.22.jar:3.4.22]
More tips and hints
To make the timeout configurable, we can use a custom config variable instead of the hard-coded duration.
To custimize the 500 status code we can either change the exception by providing a fallback to the timeout as 2nd argument and handle that exception in a controller advice - or we can just use reactors onErrorReturn.
The documentation for WebFilter actually states that they should be used to implement timeouts:
Contract for interception-style, chained processing of Web requests that may be used to implement cross-cutting, application-agnostic requirements such as security, timeouts, and others.
Still I think it is expected that spring provides such implementation out-of-the box that can be easily configured. Maybe we oversaw that it is there, but then I would argue it is too hard to find. ^^
Alternative solution path
As an alternative, we could use circuit breakers. However, those are implemented on the readers side and conceptually are used to protect the reading side against failure of the downstream - rather than protecting the internal processing within the downstream from running too long. They can only be applied to mimic a request timeout when applying them in a dedicated server (e.g. a spring cloud gateway server) that sits between the actual client and the actual service. When using Resilience4j as implementation, you can use TimeLimiter to achieve it.
I'm starting a new Spring Boot project using Spring Cloud Gateway: I need to implement a proxy application for an existing REST API.
The proxy application will implement new features (endpoints) while forwarding to the "old" application all the requests sent to existing endoints.
(Then I'll gradually move also the existing endpoints to the new application, following an approach similar to Strangler Pattern)
But I also need to rewrite the path of several existing endpoints, something like:
return routeLocatorBuilder.routes()
.route(p -> p
.path("/new-endopint")
.map("/old-endpoint") // <= is there something like 'map' method?
.uri("http://old-app-url")).build();
Is this possible? Is there some way to map an endpoint to another?
In cloud gateway there is a org.springframework.cloud.gateway.route.RouteDefinition that can map an incoming request to an upstream by applying FilterDefinition and PredicateDefinition.
You can see how this works by having a look at org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator.
So a simple RouteDefinitionLocator e.g. InMemoryRouteDefinitionRepository could solve your use case.
If you want to stay with the high level api, then org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder.RouteSpec#predicateBuilder (org.springframework.cloud.gateway.route.builder.GatewayFilterSpec#rewritePath ...) looks promising.
return routeLocatorBuilder.routes()
.routes()
.route(
p ->
p.path("/new-endpoint/**")
.filters(
spec ->
spec.rewritePath(
"/new-endpoint/(?<segment>.*)", "/old-endpoint/${segment}"))
.uri("http://old-app-url"))
.build();
When using Spring Boot health actuator
http://localhost:8080/health
{"status":"UP","diskSpace":{"status":"UP","total":122588196864,"free":59227926528,"threshold":10485760},"mongo":{"status":"UP","version":"3.2.6"}}
Now I want to check for other condition, so as to check dependent action are triggered when status is down. I want something like
{"status":"Down"}
You can write your custom 'Health Indicator' which would override the default Health Indicator and write your implementation (e.g. Always return status as down).
Now, as this is only needed to test the app, I would recommend annotating this with #Profile so that it only gets activated when the app is started with let's say test profile, e.g.:
#Component
#Profile("test")
public class MyHealthIndicator implements HealthIndicator {
By this way, if you start the app with any profile other than test, default HealthIndicator will be used.
First disable the default /health endpoint or customize it to some different endpoint. You can disable it follow
endpoints.health.disabled=true
Once this is disabled, implement your own custom endpoints at /health and define your custom conditions with whatever you like to check.
You can take a look here for creating custom endpoints
Don't forget to use test profile while creating custom endpoint