Skip to main content

Command Palette

Search for a command to run...

Securing Your Spring Boot Applications with Keycloak in Kubernetes

Published
•5 min read
Securing Your Spring Boot Applications with Keycloak in Kubernetes
N

Software Developer

Hey there, fellow developers! 👋

After spending countless hours configuring authentication for microservices in production environments, I've put together this guide to save you from the same headaches I experienced. Today, we're diving into integrating Spring Boot applications with Keycloak for authentication in Kubernetes - a powerful combination that provides robust security while maintaining scalability.

Why This Matters

Before we jump into the technical details, let's quickly address why this setup is worth your time. Modern applications require secure authentication, and managing users, roles, and permissions manually across multiple services is a nightmare. Keycloak centralizes this, while Kubernetes gives us the scalability and resilience we need. It's a match made in DevOps heaven!

Prerequisites

  • A working Kubernetes cluster (I'm assuming you already have this set up)

  • Basic knowledge of Spring Boot and OAuth2

  • Familiarity with Keycloak concepts

  • kubectl configured to work with your cluster

  • Helm (optional but recommended)

Part 1: Deploying Keycloak to Kubernetes

Let's start by setting up Keycloak in our Kubernetes cluster:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  labels:
    app: keycloak
spec:
  replicas: 1
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
      - name: keycloak
        image: quay.io/keycloak/keycloak:20.0.3
        args: ["start-dev"]
        env:
        - name: KEYCLOAK_ADMIN
          value: "admin"
        - name: KEYCLOAK_ADMIN_PASSWORD
          valueFrom:
            secretKeyRef:
              name: keycloak-secret
              key: admin-password
        - name: KC_PROXY
          value: "edge"
        - name: KC_DB
          value: "postgres"
        - name: KC_DB_URL
          value: "jdbc:postgresql://postgres:5432/keycloak"
        - name: KC_DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: username
        - name: KC_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: password
        ports:
        - name: http
          containerPort: 8080
        readinessProbe:
          httpGet:
            path: /realms/master
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        resources:
          limits:
            memory: "1024Mi"
            cpu: "1000m"
          requests:
            memory: "512Mi"
            cpu: "500m"

And the corresponding service:

apiVersion: v1
kind: Service
metadata:
  name: keycloak
  labels:
    app: keycloak
spec:
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  selector:
    app: keycloak
  type: ClusterIP

Don't forget to create the necessary secrets:

apiVersion: v1
kind: Secret
metadata:
  name: keycloak-secret
type: Opaque
data:
  admin-password: YWRtaW4xMjM= # base64 encoded 'admin123'

Part 2: Configuring Spring Boot for Keycloak Authentication

Now let's configure our Spring Boot application to work with Keycloak. First, add these dependencies to your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Next, configure your application.yml:

spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: your-client-id
            client-secret: your-client-secret
            authorization-grant-type: authorization_code
            scope: openid
        provider:
          keycloak:
            issuer-uri: http://keycloak:8080/realms/your-realm
            user-name-attribute: preferred_username

server:
  port: 8081

Create a security configuration class:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .userInfoEndpoint(userInfo -> userInfo
                    .userAuthoritiesMapper(this::mapAuthorities)
                )
            )
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);

        return http.build();
    }

    private Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {
        Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

        authorities.forEach(authority -> {
            if (authority instanceof OidcUserAuthority) {
                OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
                OidcIdToken idToken = oidcUserAuthority.getIdToken();
                Map<String, Object> claims = idToken.getClaims();

                @SuppressWarnings("unchecked")
                Map<String, Object> resourceAccess = (Map<String, Object>) claims.get("resource_access");

                if (resourceAccess != null) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> clientResource = (Map<String, Object>) resourceAccess.get("your-client-id");

                    if (clientResource != null) {
                        @SuppressWarnings("unchecked")
                        List<String> roles = (List<String>) clientResource.get("roles");

                        if (roles != null) {
                            roles.forEach(role -> mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role)));
                        }
                    }
                }
            }
        });

        return mappedAuthorities;
    }
}

Part 3: Deploying the Spring Boot App to Kubernetes

Create a Deployment for your Spring Boot app:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-app
  labels:
    app: spring-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-app
  template:
    metadata:
      labels:
        app: spring-app
    spec:
      containers:
      - name: spring-app
        image: your-registry/spring-app:latest
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: KEYCLOAK_URL
          value: "http://keycloak:8080"
        - name: KEYCLOAK_REALM
          value: "your-realm"
        - name: CLIENT_ID
          valueFrom:
            secretKeyRef:
              name: spring-app-secret
              key: client-id
        - name: CLIENT_SECRET
          valueFrom:
            secretKeyRef:
              name: spring-app-secret
              key: client-secret
        ports:
        - containerPort: 8081
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8081
          initialDelaySeconds: 30
          periodSeconds: 10
        resources:
          limits:
            memory: "512Mi"
            cpu: "500m"
          requests:
            memory: "256Mi"
            cpu: "200m"

And the corresponding service:

apiVersion: v1
kind: Service
metadata:
  name: spring-app
spec:
  selector:
    app: spring-app
  ports:
  - port: 8081
    targetPort: 8081
  type: ClusterIP

Part 4: Setting Up Ingress for External Access

For external access, we need an Ingress controller:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: auth-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: auth.yourdomain.com
    http:
      paths:
      - path: /auth
        pathType: Prefix
        backend:
          service:
            name: keycloak
            port:
              number: 8080
      - path: /app
        pathType: Prefix
        backend:
          service:
            name: spring-app
            port:
              number: 8081

Part 5: Configuring Keycloak Realm and Client

After deploying Keycloak, you'll need to:

  1. Create a new realm (e.g., "your-realm")

  2. Create a new client for your Spring Boot app

  3. Create roles and assign them to users

  4. Create test users

Part 6: Troubleshooting Common Issues

Hey there! 👋 If you're reading this section, chances are you've run into some issues. Don't worry - I've been there! Here are some common problems and solutions:

Network Connectivity Issues

If your Spring Boot app can't connect to Keycloak:

  • Ensure both pods are in the same namespace

  • Check if the Keycloak service name is correctly referenced in your Spring Boot config

  • Verify network policies aren't blocking traffic

Token Validation Failures

If you're getting token validation errors:

  • Ensure Keycloak's issuer URI matches exactly in your Spring configuration

  • Check for time synchronization issues between pods

  • Verify that you're using the correct client secret

CORS Issues

If you're getting CORS errors:

  • Configure Keycloak's client settings to allow your app's origin

  • Add appropriate CORS configuration to your Spring Boot application

Conclusion

Phew! That was quite a journey. 🎉 By now, you should have a fully functioning Spring Boot application authenticated through Keycloak, all running smoothly in Kubernetes. This setup not only provides robust security but also scales well as your application grows.

I'd love to hear how this setup works for you! Drop a comment below or reach out if you have any questions. Remember, security is a continuous process, so keep your Keycloak and Spring Boot dependencies updated.

Happy coding! 💻

More from this blog

Nikhil Soman Sahu

47 posts

Sr. Java backend developer with a passion for building scalable, efficient systems. Specializing in Spring Framework, RESTful APIs, and database optimization.