Spring Boot Admin client POST to admin/instances with configured user and password raise 403 forbidden for all clients - spring-boot

I've integrated SBA to the project. Without any authentication all clients register for SBA server and I can see all actuator endpoints, which I've opened.
I wish to have login page at SBA admin page and wish open registration only for SBA clients with proper permissions. I've added user/password properties at both sides, according to SBA documentation.
If I just add at SBA server user and password, and the same user and password I also configure for all SBA clients, all clients got responses 403 FORBIDDEN Failed to register application as Application()
SBA server part:
application.properties
spring.security.user.name=admin
spring.security.user.password=admin
spring.boot.admin.client.instance.metadata.user.name=admin
spring.boot.admin.client.instance.metadata.user.password=admin
server.port=8090
management.endpoints.web.exposure.include=
management.endpoints.web.base-path=/_manage
management.endpoints.jmx.exposure.include=
management.endpoint.shutdown.enabled=true
management.server.port=8090
security configuration with opened permissions to /instances
#Configuration
#ConditionalOnProperty(value = "spring.security.enabled", havingValue = "true")
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.permitAll();
http
.logout().logoutUrl("/logout");
http
.csrf().disable();
http
.authorizeRequests()
.antMatchers("/login.html", "/** ** /** .css", "/img/** ** ", "/third-party/** ** ")
.permitAll();
http
.authorizeRequests()
.antMatchers("/instances")
.permitAll();
//.authenticated();
http.httpBasic();
}
}
SBA client has the same user and password, to be able to register at SBA server:
application.properties
instance.name=clientdemo
feed.generation.type=CLIENTDEMO
spring.boot.admin.url=http://localhost:8090
spring.boot.admin.client.url=http://localhost:8090
spring.boot.admin.client.username=admin
spring.boot.admin.client.password=admin
spring.security.user.name=admin
spring.security.user.password=admin
server.port=8091
# ACTUATOR
management.endpoints.web.exposure.include=*
#management.endpoints.web.base-path=/_manage
management.endpoint.shutdown.enabled=true
management.server.port=8091
As result at SBA client side I see from the logs an error :
: Writing [Application(name=demo-worker, managementUrl=http://http://localhost:8091/actuator, healthUrl=http://localhost:8091/actuator/health, serviceUrl=http://localhost:8091/)] as "application/json"
: sun.net.www.MessageHeader#4089d6fd8 pairs: {POST /instances HTTP/1.1: null}{Accept: application/json}{Content-Type: application/json}{Authorization: Basic YWRtaW46YWRtaW4=}{User-Agent: Java/1.8.0_211}{Host: localhost:8090}{Connection: keep-alive}{Content-Length: 267}
: sun.net.www.MessageHeader#50cf05ff11 pairs: {null: HTTP/1.1 403}{Content-Type: text/plain}{Cache-Control: no-cache, no-store, max-age=0, must-revalidate}{Pragma: no-cache}{Expires: 0}{X-Content-Type-Options: nosniff}{X-Frame-Options: DENY}{X-XSS-Protection: 1 ; mode=block}{Referrer-Policy: no-referrer}{Transfer-Encoding: chunked}{Date: Wed, 13 Nov 2019 12:48:32 GMT}
: Response 403 FORBIDDEN
: Failed to register application as Application(name=demo-worker, managementUrl=http://localhost:8091/actuator, healthUrl=http://localhost:8091/actuator/health, serviceUrl=http://localhost:8091/) at spring-boot-admin ([http://localhost:8090/instances]): 403 null
: HTTP POST http://localhost:8090/instances
: Accept=[application/json, application/*+json]
And from SBA server side I see at the logs :
{"name":"demo-worker","managementUrl":"http://localhost:8091/actuator","healthUrl":"http://localhost:8091/actuator/health","serviceUrl":"http://localhost:8091/","metadata":o.a.c.a.AuthenticatorBase: Security checking request POST /instances
My SBA server is registered at port 8090 and SBA client is registered at port 8091.

you should add this:
.antMatchers(HttpMethod.POST, "/actuator/**")
.permitAll()

Related

Spring boot keycloak integration OAuth 2.0 Access Token Response: 401 Unauthorized: [no body]

Update: disabling client auth and authorization will not show the 401 message anymore but secured endpoints will return 403.
I'm trying to integrate keycloak core-version: 20.0.2 running from a docker container.
I've started out following https://www.baeldung.com/spring-boot-keycloak
but after multiple suggestions from different threads I cannot get the integration to work.
application properties:
keycloak.auth-server-url=localhost:1337
keycloak.realm=SARServices
keycloak.resource=sar-login
keycloak.public-client=false
keycloak.principal-attribute=preferred_username
keycloak.credentials.secret=NvkAdEPqbN39ubjqtrjn7dKElgLlUNLj
spring.security.oauth2.client.registration.keycloak.client-id=sar-login
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid
spring.security.oauth2.client.provider.keycloak.issuer-uri=https://localhost:1337/realms/SARServices
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
WebConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
class SecurityConfig(
private val keycloakLogoutHandler: KeycloakLogoutHandler
) {
#Bean
fun keycloakConfigResolver(): KeycloakConfigResolver? {
return KeycloakSpringBootConfigResolver()
}
#Bean
protected fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy {
return RegisterSessionAuthenticationStrategy(SessionRegistryImpl())
}
#Bean
#Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user/*")
.hasRole("adventurer")
.anyRequest()
.permitAll()
http.oauth2Login()
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler)
.logoutSuccessUrl("/")
return http.build()
}
}
keycloak server log:
2023-01-07 18:46:55 2023-01-07 17:46:55,045 WARN [org.keycloak.events] (executor-thread-65) type=CODE_TO_TOKEN_ERROR, realmId=7eec0241-e69f-4d5c-8b7f-7a96926e8315, clientId=sar-login, userId=null, ipAddress=172.18.0.1, error=invalid_client_credentials, grant_type=authorization_code
docker compose:
version: '1'
services:
postgresql:
image: docker.io/bitnami/postgresql:latest
environment:
- ALLOW_EMPTY_PASSWORD=yes
- POSTGRESQL_USERNAME=bn_keycloak
- POSTGRESQL_DATABASE=bitnami_keycloak
volumes:
- 'postgresql_data:/bitnami/postgresql'
- './certs:/etc/codelance/cert'
keycloak:
image: quay.io/keycloak/keycloak:latest
command: start --hostname-port=1337
ports:
- "1337:8443"
environment:
- KC_HOSTNAME=localhost
- KC_HTTPS_CERTIFICATE_FILE=/etc/codelance/cert/keycloakcert.pem
- KC_HTTPS_CERTIFICATE_KEY_FILE=/etc/codelance/cert/keycloak.pem
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=password
depends_on:
- postgresql
volumes:
- './certs:/etc/codelance/cert'
volumes:
postgresql_data:
driver: local
Web response login after signing in:
client config:
First, Keycloak adapters for Spring are deprecated. Don't use it.
Are you sure you want a client and not a resource-server? In other words, are your controller methods returning template names or JSON payloads?
My answer to this other question contains the solution for both client and resource-server: Use Keycloak Spring Adapter with Spring Boot 3

Spring Boot: OAuth endpoint redirects to 8443

I have a web-app running with SSL only (no http allowed) on port 8080:
server:
ssl:
key-store-type: PKCS12
key-store: file:${SERVER_KEYSTORE_PATH}
key-store-password: ${SERVER_CERT_PASSWORD}
port: 8080
When I launch the app, I see in the logs:
2020-03-17 17:32:29.836 INFO 90960 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (https)
One of my endpoints (the root one, actually) is protected by Spring Security, OAuth2:
#Override
protected void configure(HttpSecurity http) throws Exception {
String baseURI = redirectURI.substring(redirectURI.lastIndexOf("/"));
http.csrf().disable()
.authorizeRequests()
.antMatchers("/").authenticated()
.antMatchers("/**").permitAll()
.and()
.oauth2Login()
.defaultSuccessUrl("/")
.redirectionEndpoint().baseUri(baseURI);
}
The problem is that when I go to https://localhost:8080 - it redirects me to https://localhost:8443/oauth2/authorization/oauth So, for some reason the port 8080 is overriden with 8443.
As far as I understood from similar questions, this is happening because Tomcat is trying to redirect the user to the SSL-enabled endpoint (https) from the plain endpoint (http). But my endpoint is already SSL-enabled, so I don't really understand why is it happening. If I go to any other endpoint, it works with https and 8080 port - so only protected endpoint is problematic.
I tried to customize TomcatServletWebServerFactory with ConnectorCustomizers and set there redirect port to 8080, but it didn't help.
Any ideas on how to disable this useless redirect?
As per https://github.com/spring-projects/spring-security/issues/8140#issuecomment-600980028 :
#Override
protected void configure(HttpSecurity http) throws Exception {
PortMapperImpl portMapper = new PortMapperImpl();
portMapper.setPortMappings(Collections.singletonMap("8080","8080"));
PortResolverImpl portResolver = new PortResolverImpl();
portResolver.setPortMapper(portMapper);
LoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint(
"/login");
entryPoint.setPortMapper(portMapper);
entryPoint.setPortResolver(portResolver);
http
.exceptionHandling()
.authenticationEntryPoint(entryPoint)
.and()
//...
;
}

Spring Boot with OAuth2 behind reverse proxy

I'm new with Spring Security and trying to develop Spring Boot app with Google login using OAuth2 which runs under hostname:8080. This app is behind Apache reverse proxy server https://url.com.
Spring Boot version 2.1.0
Spring Security version 5.1.1
build.gradle:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.security:spring-security-oauth2-client")
implementation("org.springframework.security:spring-security-oauth2-jose")
}
application.yml:
oauth2:
client:
registration:
google:
clientId: <clientId>
clientSecret: <clientSecret>
scope: profile, email, openid
server:
use-forward-headers: true
servlet:
session:
cookie:
http-only: false
Spring Security config:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login();
}
}
I request https://url.com
Get redirected to https://accounts.google.com/signin/oauth/
When authenticated get redirected back to
https://url.com/login/oauth2/code/google?state={state}&code={code}&scope=openid+email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.me&authuser=0&session_state={session_state}&prompt=none which timed out with error:
[invalid_token_response] An error occurred while attempting to
retrieve the OAuth 2.0 Access Token Response: I/O error on POST
request for "https://www.googleapis.com/oauth2/v4/token": Connection
timed out (Connection timed out); nested exception is
java.net.ConnectException: Connection timed out (Connection timed out)
Is this error caused by the proxy server settings or boot app? Thanks for help.
Solved. I had to set the JVM parameters:
https.proxyHost=[host]
https.proxyPort=[port]
http.proxyHost=[host]
http.proxyPort=[port]

Migration to Spring Boot 2 from 1.5.7 - Request method POST not supported - csrf already disabled

We've migrated our software from spring boot 1.5.7 to spring boot 2.
We're using JSF by including joinfaces-parent in our pom.xml.
At the startup, all works perfectly, but login call does not work:
Request method 'POST' not supported
It is probably a Spring Security issue? CSRF is already disabled.
Here's our SecurityConfig file:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) {
try {
http.csrf().disable().authorizeRequests()
.antMatchers("/javax.faces.resource/**", Page.LOGIN.getUrlForSecurityContext())
.permitAll()
.and()
........
// *** login configuration
.formLogin()
.loginPage(Page.LOGIN.getUrlForSecurityContext()).permitAll()
.failureUrl(Page.LOGIN.getUrlForSecurityContext() + "?error=true")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(authenticationSuccessHandler)
.and()
...........
// #formatter:on
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
.......
}
The login request does not arrives to our backend.
I found out that this error is generated from the dispatcher.forward function, called from xhtml. Here the function:
public void login() throws ServletException, IOException {
final ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
final RequestDispatcher dispatcher = ((ServletRequest) context.getRequest()).getRequestDispatcher("/login");
dispatcher.forward((ServletRequest) context.getRequest(), (ServletResponse) context.getResponse());
FacesContext.getCurrentInstance().responseComplete();
}
Here more logs when the error message happens:
[io.undertow.servlet] (default task-3) Initializing Spring FrameworkServlet 'dispatcherServlet'
16:02:20,926 INFO [org.springframework.web.servlet.DispatcherServlet] (default task-3) FrameworkServlet 'dispatcherServlet': initialization started
16:02:20,938 INFO [org.springframework.web.servlet.DispatcherServlet] (default task-3) FrameworkServlet 'dispatcherServlet': initialization completed in 12 ms
16:02:20,949 WARN [org.springframework.web.servlet.PageNotFound] (default task-3) Request method 'POST' not supported
16:02:20,973 ERROR [org.springframework.boot.web.servlet.support.ErrorPageFilter] (default task-3) Cannot forward to error page for request [/login] as the response has already been committed. As a result, the response may have the wrong status code. If your application is running on WebSphere Application Server you may be able to resolve this problem by setting com.ibm.ws.webcontainer.invokeFlushAfterService to false
Thanks in advice!
Spring Security configuration looks ok for me. There is something wrong with your login controller. I suppose your login method is called in response to POST request from the client. Then it tries to forward this POST to render login page and finally throws an exception. Obviously it should be GET request instead of POST.

Spring Boot Oauth2 token request - Bad client credentials from BasicAuthenticationFilter

Requesting help on a Spring Boot OAuth2 app's BadCredentialsException when , after user authentication and approval, my Oauth2 client app requests a token from the token endpoint of the OAuth2 AuthServer.
The Spring Boot OAuth2 applications are based off the now famous Dave Syer Spring Boot/ Oauth2 UI Client/Oauth2 AuthServer/JWT example https://github.com/spring-guides/tut-spring-security-and-angular-js/tree/master/oauth2
This is in the Client Apps' debug:
DEBUG org.springframework.web.client.RestTemplate - Created POST request for "authserver/uaa/oauth/token"
DEBUG org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider - Encoding and sending form: {grant_type=[authorization_code], code=[xxxxx], redirect_uri=[oauthclientapp/login]}
DEBUG org.springframework.web.client.RestTemplate - POST request for "authserver/uaa/oauth/token" resulted in 200 (null)
DEBUG org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Could not obtain access token
This is the AuthServer's debug:
DEBUG org.springframework.security.web.FilterChainProxy - /oauth/token at position 9 of 15 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
DEBUG org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Basic Authentication Authorization header found for user 'clientID'
DEBUG org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
DEBUG org.springframework.security.authentication.dao.DaoAuthenticationProvider - User 'clientID' not found
DEBUG org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
The Oauth2 client app is just like the one in the example, no customization in the token request process, just whatever #EnableOAuth2Sso gives us. The ClientDetails config on the AuthServer is also just like the example, sample below, so nothing special.
Any suggestions to better troubleshoot this are much appreciated. Thanks.
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("clientID")
.secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token",
"password").scopes("openid");
}
I had the same error with your config when sending POST request to /oauth/token endpoint like this:
curl localhost:8080/oauth/token -d grant_type=authorization_code -d client_id=acme -d redirect_uri=http://localhost:4200/redirectUrl -d code=5chf5f
My intention was to use authorization code flow and I didn't want to provide the client_secret parameter which caused the error
-d client_sercret=acmesecret
If your situation is the same you may want to add another client with no secret for authorization code flow:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("clientWithSecret")
.secret("acmesecret")
.authorizedGrantTypes("password")
.scopes("openid")
.and()
.withClient("clientNoSecret")
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("openid");
}

Resources