Adding Spring Session Redis grows the context by 81 beans and swaps sessionRepository for RedisSessionRepository, while authorizedClientService stays InMemoryOAuth2AuthorizedClientService. The user then reads as logged in with principal labuser while accessTokenStoredOnServer is false, which is worse than being logged out. Redis holds only the security context, serialized with Java native serialization, and the refresh token is not there to encrypt in the first place. Three problems on the way: Kubernetes service links overwrote REDIS_PORT with a tcp:// URL, the tests tried to reach Redis, and the resource server was never deployed so a DNS failure looked like a token failure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
182 lines
6.3 KiB
YAML
182 lines
6.3 KiB
YAML
# BFF (2 replicas) + Redis, for the B-layer experiments.
|
|
#
|
|
# The BFF is deployed FIRST WITHOUT any session store wiring. That is deliberate:
|
|
# B-0 asks what Spring Boot's autoconfiguration actually picks when nothing is
|
|
# configured, and the only honest way to answer is to look at a running instance
|
|
# that has been given nothing. Redis is deployed alongside but left unused until
|
|
# B-1 turns it on.
|
|
#
|
|
# kubectl apply -f deploy/lab/k8s/bff-redis.yaml
|
|
#
|
|
# Image comes from the workstation, not a registry:
|
|
# docker build -t keycloak-pattern-bff:lab bff/
|
|
# docker save keycloak-pattern-bff:lab | ssh test-server "ssh kc-lab-1 'sudo k3s ctr images import -'"
|
|
# (repeat for kc-lab-2)
|
|
# so imagePullPolicy must stay Never on both replicas.
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: bff-secrets
|
|
namespace: keycloak-lab
|
|
type: Opaque
|
|
stringData:
|
|
# Matches the client created with kcadm in the keycloak-patterns realm.
|
|
# Base64 in etcd is not encryption — see D-3.
|
|
KEYCLOAK_CLIENT_SECRET: bff-lab-secret
|
|
---
|
|
# Redis. No persistence yet: `--save ""` and no appendonly, so a restart loses
|
|
# everything. B-5 and B-6 compare that against RDB and AOF, which is easier to
|
|
# reason about when the starting point is "nothing survives".
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: redis
|
|
namespace: keycloak-lab
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels: { app: redis }
|
|
template:
|
|
metadata:
|
|
labels: { app: redis }
|
|
spec:
|
|
# Same node as postgres so a node-loss experiment takes both stores at
|
|
# once, matching how A-4 was set up.
|
|
nodeSelector:
|
|
kubernetes.io/hostname: kc-lab-2
|
|
containers:
|
|
- name: redis
|
|
image: redis:7.4-alpine
|
|
args: ["redis-server", "--save", "", "--appendonly", "no"]
|
|
ports:
|
|
- containerPort: 6379
|
|
name: redis
|
|
readinessProbe:
|
|
exec: { command: ["redis-cli", "ping"] }
|
|
initialDelaySeconds: 3
|
|
resources:
|
|
requests: { memory: 32Mi, cpu: 20m }
|
|
limits: { memory: 128Mi }
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: redis
|
|
namespace: keycloak-lab
|
|
spec:
|
|
selector: { app: redis }
|
|
ports:
|
|
- port: 6379
|
|
targetPort: redis
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bff
|
|
namespace: keycloak-lab
|
|
spec:
|
|
# Two replicas is the whole point: Q1 and Q2 only exist because a request can
|
|
# land on an instance that did not handle the login.
|
|
replicas: 2
|
|
selector:
|
|
matchLabels: { app: bff }
|
|
template:
|
|
metadata:
|
|
labels: { app: bff }
|
|
spec:
|
|
# Spread across both nodes so "the other instance" is genuinely another
|
|
# machine, not another process on the same kernel.
|
|
topologySpreadConstraints:
|
|
- maxSkew: 1
|
|
topologyKey: kubernetes.io/hostname
|
|
whenUnsatisfiable: ScheduleAnyway
|
|
labelSelector:
|
|
matchLabels: { app: bff }
|
|
# 쿠버네티스는 같은 네임스페이스의 Service 마다 Docker link 시절의
|
|
# 환경변수를 자동 주입한다: REDIS_PORT=tcp://10.43.57.116:6379.
|
|
# 그것이 application.yml 의 ${REDIS_PORT:6379} 를 덮어써서 기동이 실패했다.
|
|
# Failed to bind properties under 'spring.data.redis.port' to int:
|
|
# Value: "tcp://10.43.57.116:6379"
|
|
# 이 주입 자체를 끄는 것이 근본 처방이다. 이름을 바꿔 피하면 다음 사람이
|
|
# 같은 함정에 다시 빠진다.
|
|
enableServiceLinks: false
|
|
containers:
|
|
- name: bff
|
|
image: keycloak-pattern-bff:lab
|
|
imagePullPolicy: Never
|
|
ports:
|
|
- containerPort: 8083
|
|
name: http
|
|
env:
|
|
# The browser is redirected to the public name; the BFF calls the
|
|
# token endpoint over the cluster network. Getting these two the same
|
|
# way round is what the 2-hop header experiment was about.
|
|
- name: KC_ISSUER_EXTERNAL
|
|
value: https://auth.hyeonworks.com/realms/keycloak-patterns
|
|
- name: KC_ISSUER_INTERNAL
|
|
value: http://keycloak.keycloak-lab.svc:8080/realms/keycloak-patterns
|
|
# echo 는 header-lab 네임스페이스의 8081 이다. 다른 네임스페이스의
|
|
# 서비스는 <svc>.<ns>.svc 로 부른다. 이름을 틀리면 500 이 나는데
|
|
# 원인은 UnresolvedAddressException 이지 토큰 문제가 아니다.
|
|
- name: RESOURCE_API_BASE_URL
|
|
value: http://echo.header-lab.svc:8081
|
|
- name: KEYCLOAK_CLIENT_SECRET
|
|
valueFrom:
|
|
secretKeyRef: { name: bff-secrets, key: KEYCLOAK_CLIENT_SECRET }
|
|
# Spring needs to know it is behind TLS termination, for the same
|
|
# reason Keycloak needs KC_PROXY_HEADERS. Without it the redirect_uri
|
|
# it builds comes back as http:// and Keycloak rejects it.
|
|
- name: SERVER_FORWARD_HEADERS_STRATEGY
|
|
value: native
|
|
# B-1: Application Session 을 Redis 로 옮긴다.
|
|
# OAuth2AuthorizedClient 는 이것으로 옮겨지지 않는다 — 조회 키가
|
|
# 다르기 때문이며, B-0 에서 확인한 사실이다.
|
|
- name: SPRING_SESSION_STORE_TYPE
|
|
value: redis
|
|
- name: REDIS_HOST
|
|
value: redis.keycloak-lab.svc
|
|
- name: REDIS_PORT
|
|
value: "6379"
|
|
- name: JAVA_TOOL_OPTIONS
|
|
value: "-Xms128m -Xmx320m"
|
|
readinessProbe:
|
|
httpGet: { path: /actuator/health/readiness, port: http }
|
|
initialDelaySeconds: 20
|
|
failureThreshold: 30
|
|
livenessProbe:
|
|
httpGet: { path: /actuator/health/liveness, port: http }
|
|
initialDelaySeconds: 60
|
|
resources:
|
|
requests: { memory: 320Mi, cpu: 100m }
|
|
limits: { memory: 512Mi }
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: bff
|
|
namespace: keycloak-lab
|
|
spec:
|
|
selector: { app: bff }
|
|
ports:
|
|
- port: 8083
|
|
targetPort: http
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: bff
|
|
namespace: keycloak-lab
|
|
spec:
|
|
ingressClassName: traefik
|
|
rules:
|
|
- host: app1.hyeonworks.com
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: bff
|
|
port:
|
|
number: 8083
|