diff --git a/deploy/reverse-proxy/keycloak.env.example b/deploy/reverse-proxy/keycloak.env.example new file mode 100644 index 0000000..8056c8c --- /dev/null +++ b/deploy/reverse-proxy/keycloak.env.example @@ -0,0 +1,6 @@ +# Keycloak receives HTTP only from the trusted reverse proxy. +KC_HTTP_ENABLED=true +KC_PROXY_HEADERS=xforwarded +KC_HOSTNAME=https://auth.example.test +KC_HOSTNAME_STRICT=true + diff --git a/deploy/reverse-proxy/nginx-keycloak.conf b/deploy/reverse-proxy/nginx-keycloak.conf new file mode 100644 index 0000000..9f68882 --- /dev/null +++ b/deploy/reverse-proxy/nginx-keycloak.conf @@ -0,0 +1,14 @@ +server { + listen 8080; + server_name auth.example.test; + + location / { + proxy_pass http://keycloak:8080; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port 443; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} diff --git a/docs/reverse-proxy-headers.md b/docs/reverse-proxy-headers.md new file mode 100644 index 0000000..902b816 --- /dev/null +++ b/docs/reverse-proxy-headers.md @@ -0,0 +1,15 @@ +# Reverse proxy headers + +TLS를 reverse proxy에서 종료하면 Keycloak은 브라우저가 사용한 외부 origin을 +정확히 알아야 한다. 배포 예제는 다음 계약을 함께 적용한다. + +- nginx는 `Host`, `X-Forwarded-Host`, `X-Forwarded-Port`, + `X-Forwarded-Proto`, `X-Forwarded-For`를 덮어쓴다. +- Keycloak은 `KC_PROXY_HEADERS=xforwarded`로 그 헤더 형식을 명시한다. +- `KC_HOSTNAME`은 외부 HTTPS URL로 고정하고 strict hostname 검증을 켠다. +- Keycloak의 8080 포트는 public으로 publish하지 않고 proxy network에서만 + 접근시킨다. 신뢰되지 않은 클라이언트가 forwarded header를 직접 넣을 수 + 있으면 안 된다. + +`scripts/verify-reverse-proxy-headers.sh`는 양쪽 설정의 짝과 nginx 구문을 +검증한다. diff --git a/scripts/verify-reverse-proxy-headers.sh b/scripts/verify-reverse-proxy-headers.sh new file mode 100755 index 0000000..3a1e526 --- /dev/null +++ b/scripts/verify-reverse-proxy-headers.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env sh +set -eu + +config=deploy/reverse-proxy/nginx-keycloak.conf +env_file=deploy/reverse-proxy/keycloak.env.example + +grep -q 'proxy_set_header X-Forwarded-Host' "$config" +grep -q 'proxy_set_header X-Forwarded-Port 443' "$config" +grep -q 'proxy_set_header X-Forwarded-Proto https' "$config" +grep -q '^KC_PROXY_HEADERS=xforwarded$' "$env_file" +grep -q '^KC_HOSTNAME=https://' "$env_file" + +docker run --rm \ + --add-host keycloak:127.0.0.1 \ + -v "$PWD/$config:/etc/nginx/conf.d/default.conf:ro" \ + nginx:1.29-alpine nginx -t + +echo "Reverse-proxy header and Keycloak hostname contracts verified"