I have a Spring boot app with end point POST /login which validates the credentials and returns the JWT in the response header. There is another endpoint /api/cars/listing which requires Authorization header with valid JWT. This app is deployed to a Kubernetes cluster with 3 nodes. After that I have installed ngnix ingress controller for L7 routing within the cluster and added the ingress resource.
Followed this tutorial - https://cloud.google.com/community/tutorials/nginx-ingress-gke.
When I use the JWT generated from POST /login and use it for GET /api/cars/listings I am getting 403 error in the response. Is there anything that I need to configure in the Nginx ingress controller for routing the request to the same node based on the request IP?
kind: Ingress
metadata:
name: ingress-resource
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- path: /jwt(/|$)(.*)
backend:
serviceName: jwt-app-service
servicePort: 80
POST /jwt/login
GET /jwt/api/cars/listings
After looking at the kubectl logs, found the issue was related to JWT secret key generation. Everytime the spring boot app restarted the secret key got dynamically generated.
I was using Keys.secretKeyFor(SignatureAlgorithm.HS512);
in the Spring config file as below. This could be configured as deployment env variable or in some other secured way.
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtTokenService jwtTokenService;
private AppUserDetailsService appUserDetailsService;
@Autowired
public SecurityConfig(AppUserDetailsService appUserDetailsService) {
this.jwtTokenService = jwtTokenService();
this.appUserDetailsService = appUserDetailsService;
}
public SecurityConfig() {
this.jwtTokenService = jwtTokenService();
}
private Key base64EncodedSecretKey() {
return Keys.secretKeyFor(SignatureAlgorithm.HS512);
}
private JwtTokenService jwtTokenService() {
return new JwtTokenService(base64EncodedSecretKey());
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.appUserDetailsService)
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/greetings").permitAll()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and()
.addFilterBefore(new LoginFilter("/login", this.jwtTokenService, authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtAuthenticationFilter(this.jwtTokenService, "/api/**"), UsernamePasswordAuthenticationFilter.class);
}
}