[keycloak 맛보기 #7] Production Keycloak을 위한 설정
들어가기전에,
*https://www.keycloak.org/server/configuration-production
Keycloak은 수백~수천명의 사용자에게 안전한 인증 및 권한부여를 제공할 수 있도록 설계되어있다.
안전하고 안정적인 Keycloak을 프로덕션 환경에 배포하기 위해 설정해야 할 부분들을 확인 해 보자.
1. Keycloak의 Hostname 설정
* 공식문서: https://www.keycloak.org/server/hostname
✓ Hostname을 설정해야하는 이유
keycloak은 보안상의 이유로 hostname 설정을 필수로한다.
▪︎ 설명
Keycloak은 OIDC Discovery 엔드포인트, 사용자 비밀번호 변경 엔드포인트 등의 URL이 외부로 공개된다.
이때, hostname이 설정되어있지 않다면 가짜 도메인으로 redirect하여 사용자 정보(token, password)를 탈취 당할수도 있다.
따라서 Hostname을 명시적으로 설정하여 다른 도메인으로 토큰이 발급되는것을 방지할 수 있다.
keycloak의 보안 강화를 위해 아래 3가지를 기본적으로 설정하는것이 바람직하다.
- 항상 https 사용
- 관리자 콘솔 분리
- 프록시 설정 시 신뢰할수있는 프록시 주소 설정
▪︎ 탈취 시나리오
공격자가 조작된 Host헤더를 사용하여 Keycloak Redirect URL을 생성할때 악의적인 도메인을 설정할 수 있다.
사용자가 my.keycloak.com에서 로그인을 시도
↓
공격자가 Host 헤더를 fake-site.com으로 변조
↓
리다이렉트 URL이 fake-site.com으로 생성됨
↓
사용자가 피싱 사이트로 리다이렉트됨
혹은 리버스 프록시 설정도 오용 될 수 있다.
리버스 프록시 설정은 뒤에서 조금 더 다뤄보도록 한다.
Client -> Reverse Proxy -> Keycloak
↑
|
공격자가 X-Forwarded-Host
헤더를 조작
✓ 세가지 주요 엔드포인트 그룹
Keycloak은 각각 다른 목적을 가진 3가지 주요 엔드포인트가 있다.
일반적인 환경에서 어떻게 쓰이는지 좀 더 확인해보자.
▪︎ frontend
사용자 및 애플리케이션이 직접 접근하는 공개 엔드포인트다.
로그인/로그아웃/비밀번호 재설정 URL 또는 세션 관리와 관련된 엔드포인트 등이있다.
bin/kc.[sh|bat] start --hostname https://auth.my.com
▪︎ Backend
클라이언트 애플리케이션과 Keycloak간의 내부통신을 위한 엔드포인트다.
인증 엔드포인트, 토큰 및 토큰 인트로스펙션 엔드포인트, userinfo 엔드포인트, JWKS URI 엔드포인트 등이 있다.
기본적으로는 hostname-backchannel-dynamic 옵션이 false로 설정되어있다.
여기서 조금 헷갈릴 수 있는 부분은 hostname-backchannel-dynamic 옵션 설정을 true로 변경한다면
내부 통신을 위한 내부 endpoint가 활성화 된다는 뜻이라는 거다.
서브도메인을 별도로 설정할 수 있는것이 아니다.
hostname-backchannel-dynamic=False | hostname-backchannel-dynamic=True |
- 백채널 통신도 --hostname 값 사용 - 내부 서비스간 통신도 외부 URL을 통해 이루어짐 |
- 백채널 통신은 실제 요청이 들어온 호스트/IP 사용 - 내부 통신 최적화 가능 |
백엔드 엔드포인트는 아래 설정으로 활성화 할 수있다.
bin/kc.[sh|bat] start --hostname https://auth.my.com --hostname-backchannel-dynamic true
▪︎ Administrator
관리용 콘솔 엔드포인트다.
별도의 호스트 네임을 설정할 수 있다.
bin/kc.[sh|bat] start --hostname https://auth.my.com --hostname-admin https://admin.auth.my.com:8443
2. Keycloak TLS 활성화
* 공식문서: https://www.keycloak.org/server/enabletls, https://www.keycloak.org/server/keycloak-truststore
너무나 당연하게도, 외부 인터넷으로 노출된 Keycloak엔드포인트는 반드시 https/TLS를 사용해야 한다.
다만, AWS를 사용하는 경우 ALB에서 SSL을 해제하여 외부 노출된 엔드포인트는 TLS설정을, 내부 네트워크에서는 평문통신을 사용하기도 한다.
Keycloak은 PEM 형식의 파일이나 Java Keystore에서 필요한 인증서 인프라를 로드하도록 구성할 수 있으며,
두 가지 모두 설정된 경우 PEM 파일이 Java Keystore보다 우선한다.
▪︎ AWS ALB(+ACM)에서 TLS/SSL 오프로드
[클라이언트] → HTTPS → [ALB(+ACM)] → HTTP → [Keycloak]
bin/kc.[sh|bat] start \
--hostname auth.my.com \
--proxy edge \
--http-enabled=true \
--http-port=8080
- 인증서 관리가 AWS AWS Certificate Manager(ACM)로 간편화
- ACM에서 인증서 자동갱신
- ALB에서의 TLS/SSL 오프로딩으로 Keycloak 서버 부하 감소
▪︎ End to End TLS/SSL 사용
[클라이언트] → HTTPS → [ALB] → HTTPS → [Keycloak]
# PEM 파일 사용방식
bin/kc.[sh|bat] start \
--hostname auth.my.com \
--proxy passthrough \
--https-certificate-file=/path/to/cert.pem \
--https-certificate-key-file=/path/to/key.pem \
--http-enabled=false
# Java Keystore (JKS) 방식
###1. Keystore 생성
keytool -genkeypair \
-alias keycloak \
-keyalg RSA \
-keysize 2048 \
-validity 365 \
-keystore keycloak.jks \
-dname "CN=auth.my.com" \
-storepass store123 \
-keypass key123
### 2. Keycloak 시작 명령어
bin/kc.[sh|bat] start \
--hostname auth.my.com \
--proxy passthrough \
--https-key-store-file=/path/to/keycloak.jks \
--https-key-store-password=store123 \
--https-key-store-type=JKS \
--http-enabled=false
- 전 구간 암호화로 더 높은 보안성
3. Keycloak Database 설정
* 공식문서: https://www.keycloak.org/server/db
Keycloak 서버를 구성할때 사용할 수 있는 database engine은 여러가지가 있다.
(mariadb, mssql, mysql, oracle, postgres..)
처음에 테스트로 설치할때는 dev-file 데이터베이스를 사용하기때문에, 프로덕션에 구성할땐 반드시 설정을 변경하여 사용해야 한다.
▪︎ Postgres Database를 이용한 설정 예시
conf/keycloak.conf 파일에서 Database 를 위한 설정을 할 수 있다.
# conf/keycloak.conf
db=postgres # 데이터베이스 유형(mariadb, mysql, postgres..등이 있음)
db-username=keycloak # 데이터베이스 사용자 이름
db-password=password # 사용자 패스워드
db-url-host=keycloak-postgres # DB 엔드포인트
설정파일을 수정했다면 아래 명령어로 실행할 수 있다.
bin/kc.[sh|bat] build
bin/kc.[sh|bat] start --optimized
설정파일로 추가하는것이 아니라 그냥 커멘드로도 설정할 수 있다.
bin/kc.[sh|bat] start --db postgres --db-url-host keycloak-postgres --db-username keycloak --db-password password
▪︎ 설정 옵션들과 주의사항
설정 가능한 옵션들은 여기 링크에서 확인할 수 있다.
oracle을 제외한 대부분의 데이터베이스 엔진 드라이버가 설치되어있고, 사용시 기본 드라이버를 사용하지만, 별도로 드라이버를 설정 할 수 있다.
추가적으로 주의해야할 부분은 connection pool 부분일 것이다.
프로덕션인 경우, max,min connection pool을 잘 설정해주어야 한다.
각 데이터베이스 엔진에 따라 설정 방법이 공식문서에 잘 설명되어있으니, 공식문서를 통해 구성하는것이 좋다.
4. Keycloak 다중 노드 클러스터 설정 - Cache
* 공식문서: https://www.keycloak.org/server/caching
▪︎ 아키텍쳐
Keycloak은 고가용성(HA) 및 다중 노트 클러스터링 설정을 할 수 있도록 설계되어있다.
여러대의 Keycloak노드를 사용하므로써, 특정 노드가 중단되도 장애를 방지할 수 있고, 부하에 따라 노드를 확장/축소 함으로써 가용성을 확보할 수 있다.
Keycloak 클러스터링의 핵심은 Reverse Proxy(LoadBalanceing) 및 캐시 구성이라고 할 수 있다.
Keycloak은 Infinispan을 기반으로 (key-value In-memory) 클러스터 캐시를 구성할 수 있도록 제공한다.
캐시 구성이 클러스터 설정에 어떤역할을 하는지 확인해본다.
▪︎ Cache 옵션
Keycloak의 캐시는 --cache 옵션으로 설정할 수 있고, 옵션은 local/ ispn이 있다.
- --cache=local 인 경우 모든 캐시가 로컬에서만 동작(클러스터링❌불가능):
- sessions, clientSessions도 로컬에만 저장
- realms, users 등도 로컬에만 저장
- 노드간 데이터 공유 없음
- --cache=ispn* 인 경우 클러스터 내 공유되어야하는 데이터가 노드 외부에 캐시로 저장 (클러스터링 🟢가능):
- Session 등 클러스터 전체에 공유되어야 하는 데이터가 외부 캐시에 저장됨 (아래 표 참고)
구분 | Local Cache | Distribute Cache |
목적 | 데이터베이스의 부하를 줄이기 위한 로컬 캐시 | 클러스터 전체에 공유되어야 하는 데이터 캐시 |
종류 | - realms: Realm 데이터 - users: 사용자 데이터 - authorization: 권한 데이터 - keys: 공개키 데이터 |
- authenticationSessions: 인증 프로세스 중인 세션 - sessions: 사용자 세션 - clientSessions: 클라이언트 세션 - loginFailures: 로그인 실패 기록 - actionTokens: 일회용 액션 토큰 등.. |
특징 | - 노드별로 독립적으로 동작 - 클라이언트 세션 캐시 기본 10,000개 엔트리 저장 - 다른 노드의 변경사항은 work 캐시*를 통해 무효화 |
- 세션 데이터가 특정 노드들에 분산 저장 - owners 설정에 따라 복제본 수 결정 - 노드 장애시 자동 복구 |
* ispn은 Infinispan(ispn/링크)을 말하며, Redis나 Memcache와 같은 캐시 시스템이다.
keycloak은 공식적으로 ispn만 지원한다.
* work cache는 캐시 무효화를 위한 방법이다. (링크) 간단하게 설명하자면 A,B,C 3개의 노드 를 운영할때 A노드에서 User데이터가 변경되었다고 하면, A노드는 User의 데이터가 변경되었다고 Work Cache에 무효화 메시지 전송하고, Work Cache가 모든 노드에 브로드캐스트하며 각 노드는 해당 데이터를 삭제한다. 이렇게 설정해야 데이터의 수정이 발생하더라도 모든 노드가 같은 데이터를 가지고 있을 수 있다.
▪︎ Cache 설정 방법
일반 명령어로는 아래 설정으로 클러스터를 위한 캐시를 구성할 수 있다.
bin/kc.sh start \
--cache=ispn \
--cache-stack=kubernetes \
--cache-owners=2 \
--features-disabled=persistent-user-session \
--spi-sticky-session-encoder-infinispan-should-attach-route=true \
--metrics-enabled=true \
--cache-metrics-histograms-enabled=true
- --cache=ispn: 클러스터링 활성화
- --cache-stack=kubernetes: Discovery 방법 선택 (jdbc-ping, kubernetes)
- --cache-owners*=2: 각 캐시 엔트리 복제본 수
- --features-disabled=persistent-user-session: 세션 DB저장 비활성화
- --spi-sticky-session-encoder-infinispan-should-attach-route=true : 스티키 세션 활성화, 세션 소유 노드로 요청 라우팅, 캐시 접근 최적화
- --metrics-enabled=true: 모니터링 활성화
- --cache-metrics-histograms-enabled=true: 캐시 메트릭 히스토그램 활성화
* --cache-owners 설정은 엔트리(데이터)의 복제본을 몇개 사용할건지 설정한다.
이 설정은 너무 큰 값으로 설정하면 네트워크,CPU등에 부하를 줄 수 있으므로 , 일반적으로 2~3개의 복제본을 권장한다. owners=2는 한 노드 장애를, owners=3은 두 노드 장애를 허용할 수 있다.
bitnami에서 제공하는 keycloak helm chart 설정으로는 아래와 같이 설정된다. (특별히 커스텀할게 없다)
# https://artifacthub.io/packages/helm/bitnami/keycloak?modal=values
## Keycloak cache configuration
## ref: https://www.keycloak.org/server/caching
## @param cache.enabled Switch to enable or disable the keycloak distributed cache for kubernetes.
## NOTE: Set to false to use 'local' cache (only supported when replicaCount=1).
## @param cache.stackName Set infinispan cache stack to use
## @param cache.stackFile Set infinispan cache stack filename to use
## @param cache.useHeadlessServiceWithAppVersion Set to true to create the headless service used for ispn containing the app version
##
cache:
enabled: true
stackName: kubernetes
stackFile: ""
useHeadlessServiceWithAppVersion: false
5. Keycloak 다중 노드 클러스터 설정 - Reverse Proxy
* 공식문서: https://www.keycloak.org/server/reverseproxy
▪︎ 아키텍쳐
위에서 설명한데로, Keycloak은 고가용성(HA) 및 다중 노트 클러스터링 설정을 할 수 있도록 설계되어있으며,
Keycloak 클러스터링의 핵심은 Reverse Proxy(LoadBalanceing) 및 캐시 구성이다.
이번 단계에서는 Reverse Proxy(=API Gateway, LoadBalancer) 부분을 확인해보자.
▪︎ Reverse Proxy의 역할
리버스 프록시가 해주어야 하는 역할은 아래와 같다.
구분 | 설명 |
노드 부하 분산 | - 트래픽을 여러 Keycloak 노드에 균등하게 분배한다. - 부하 분산 알고리즘을 적절하게 선택하여 사용한다. - Round Robin: 순차적 분배 - Least Connection: 연결 수가 적은 노드 우선 - Response Time: 응답 시간 기준 예시 설정 (nginx): upstream keycloak { server keycloak1:8080; server keycloak2:8080; server keycloak3:8080; } |
세션 어피니티 (Sticky Session) 공식문서 링크 |
- 캐시 히트율 향상/ 세션 데이터 접근 최적화/ 불필요한 노드간 통신 감소를 위해 동일 사용자의 요청을 같은 노드로 라우팅 - 필수 설정은 아니지만 설정하면 부하 감소 및 처리 속도에 이점이 있음 예시 설정 (nginx): upstream keycloak { ip_hash; # 또는 sticky cookie server keycloak1:8080; server keycloak2:8080; } |
헤더 전송 공식문서 링크 |
- 클라이언트 정보를 Keycloak에 전달 - 주요 헤더: - X-Forwarded-For: 실제 클라이언트 IP - X-Forwarded-Proto: 원본 프로토콜 - X-Forwarded-Host: 원본 호스트 - X-Real-IP: 실제 클라이언트 IP - --proxy-headers 옵션을 설정하지 않으면, Origin 체크 실패하고 403(Forbidden)을 반환함 예시 설정 (nginx): proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; |
SSL/TLS 처리 최적화 | - SSL/TLS 오프로드를 Reverse Proxy에서 처리할 수 있음 - "2. Keycloak TLS 활성화" 참고 |
노드 상태 모니터링 | - 타겟 노드의 상태를 모니터링하고, 비정상적인 노드에 트래픽 차단 예시 설정 (nginx): health_check uri=/health interval=5s; |
▪︎ 노출할 엔드포인트 지정
Reverse Proxy를 사용할때 모든 엔드포인트를 다 노출하는것이 아니라, 필요한 몇개의 엔드포인트만 노출하도록 설정한다.
(공식문서링크)
Keycloak Path | Reverse Proxy Path | Exposed | 설명 |
/ | ❌ | 불필요한 관리자 경로 노출될 우려가 있음 | |
/admin/ | ❌ | 불필요한 관리자 경로 노출될 우려가 있음 | |
/realms/ | /realms/ | ✅ | - OIDC 엔드포인트 필요 - 인증/인가 프로세스 필수 - 클라이언트 연동 필요 |
/resources/ | /resources/ | ✅ | - UI 자원 제공 - 정적 파일 서비스 - CDN 사용 가능 |
/metrics | ❌ | 시스템 정보/성능데이터/내부 모니터링 노출 불필요 | |
/health | ❌ | 시스템 상태 정보 노출 불필요 내부 로드밸런서용으로 사용하며, 불필요한 공격 정보가 제공됨 |
▪︎ 기타 알면 좋은것..
공식문서에는 reverse proxy로 사용할 수 있는 프록시로 nginx, haproxy, nginx를 말하지만,
AWS환경에서는 간단하게 AWS ALB를 사용하면 된다.
AWS ALB는 다른 프록시들과 비교해보자면, 기본적인 기능 (SSL/TLS 오프로드, 세션 어피니티, 헬스체크, 라우팅 알고리즘 선택 등의 기능 제공)은 똑같은데 관리할 필요가 거의 없다는 장점이 있다.