The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
126 lines
8.6 KiB
Markdown
126 lines
8.6 KiB
Markdown
---
|
|
id: 7ff40767-a00b-4db2-98f6-0cdfce8c8936
|
|
kind: QUESTION
|
|
slug: edge-authorization-scope
|
|
title: Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가
|
|
topic: oauth-oidc-auth-boundary
|
|
topicName: OAuth/OIDC 인증 경계
|
|
project: KeyCloak Patterns
|
|
status: 게시 중
|
|
version: 36
|
|
questionStatus: OPEN
|
|
studio: "https://hyeonworks.com/studio/documents/7ff40767-a00b-4db2-98f6-0cdfce8c8936/edit"
|
|
public: "https://hyeonworks.com/questions/edge-authorization-scope"
|
|
---
|
|
|
|
# Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가
|
|
|
|
현재 Edge는 인증된 사용자의 `user`와 `email`만 Header로 전달하고 있으며, Upstream 애플리케이션에서는 Role을 이용한 인가 판단을 하지 않는다.
|
|
|
|
따라서 현재 구조만으로는 Role 기반 인가가 필요한 요구사항이 추가되었을 때 어떻게 처리할지 결정되어 있지 않다.
|
|
Edge가 사용자의 Role까지 확인해 Header로 전달할지, 아니면 애플리케이션이 Role과 권한을 확인하고 인가를 직접 판단하도록 할지 별도로 결정해야 한다.
|
|
|
|
Role이나 권한처럼 애플리케이션의 기능과 밀접한 정보가 계속 늘어난다면, 이러한 정보를 Edge Header에 계속 추가하기보다 인가 책임을 애플리케이션에서 처리하는 구조가 더 적절한지도 함께 검토한다.
|
|
|
|
## 관계
|
|
|
|
- **Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유**
|
|
edge가 user와 email만 전달한다는 사실의 출처다.
|
|
- **Forward-Auth에서 Identity Header를 신뢰하기 위한 조건**
|
|
헤더 allowlist와 검증 조건이 이 기준에 있다.
|
|
- **BFF 인증 구조 설계 기준**
|
|
되돌리는 선택지의 기준이 이 문서다.
|
|
|
|
## 사실
|
|
|
|
- 지금 edge 응답은 user와 email만 전달한다. role과 groups, tenant, 인증 방식, token 만료는 전달하지 않는다.
|
|
- upstream의 identity endpoint는 role을 확인하지 않는다. 누가 왔는지만 응답한다.
|
|
- 현재 Internal Token 검증은 특정 Controller에서만 수행하고 있으며, Security 설정에서는 해당 경로를 permitAll로 허용하고 있다.
|
|
|
|
이 구조에서는 같은 내부 경로 아래에 새로운 Endpoint를 추가하더라도 Internal Token 검증이 자동으로 적용되지 않는다.
|
|
그래서 Filter, Interceptor, 또는 Spring Security의 인증 처리 단계처럼 공통 경계에서 검증하도록 옮겨야 한다.
|
|
- Nginx는 client가 보낸 동명 헤더를 merge하지 않고 덮어쓴다. 추가적으로 늘어나는 헤더도 같은 처리를 받아야 한다.
|
|
- upstream은 JWT를 입력으로 받지 않아서 헤더로 넘어온 값을 검증할 방법이 없다.
|
|
|
|
## 가정
|
|
|
|
- 헤더 종류가 늘어나면 정해야 할 계약도 늘어난다.
|
|
- role이 바뀌는 시점과 요청이 오는 시점이 달라서 그 사이에 들어온 요청은 바뀌기 전의 값을 볼 수도 있다.
|
|
|
|
## 미지수
|
|
|
|
- 다중 값 role을 어떤 구분자와 escaping으로 보낼지. 값 안에 그 구분자가 들어오면 어떻게 되는지.
|
|
- 헤더 크기 상한을 넘으면 어떻게 되는지. proxy가 자르는지 요청 자체가 거부되는지.
|
|
- role이 바뀌었을 때 proxy session과 downstream 인가가 언제 반영되는지. 권한 변경이 몇 분 뒤에 반영되는지.
|
|
- upstream이 헤더 존재만 볼지 값과 service identity까지 볼지.
|
|
|
|
## 제약
|
|
|
|
- 전달할 헤더는 allowlist로 해야 하고 client가 보낸 동명 헤더는 항상 덮어써야 한다.
|
|
- internal token 검사가 controller 한 곳에만 있다. 헤더를 늘리기 전에 이 검사를 공통 경계로 옮겨야 된다.
|
|
|
|
## 선택지
|
|
|
|
### 1. 인증만 edge에 둔다
|
|
|
|
Edge가 전달하는 Header를 user와 email 정도로 제한하면 Edge와 Upstream 사이의 계약을 작게 유지할 수 있다.
|
|
Role이나 Permission 정보를 Header에 계속 추가하지 않으므로 Header 크기가 커지는 문제도 줄일 수 있다.
|
|
|
|
이 경우 인가 판단은 각 Upstream 애플리케이션이 직접 수행한다.
|
|
애플리케이션은 전달받은 사용자 식별 정보를 기준으로 자신의 저장소에서 Role이나 Permission을 조회하고,
|
|
해당 요청을 허용할지 결정해야 한다.
|
|
|
|
이 구조에서는 서비스마다 권한 조회와 인가 로직을 별도로 구성해야 한다.
|
|
따라서 Edge의 책임은 단순하게 유지할 수 있지만, 서비스 수가 늘어나면 각 서비스에서 동일하거나 유사한 권한 조회 체계를 반복해서 구현하고 운영해야 할 수 있다.
|
|
|
|
### 2. role 전달까지 edge에 둔다
|
|
|
|
Edge가 공통 Role 정보를 확인해 Upstream에 전달하면 각 서비스가 별도로 사용자 권한을 조회해야 하는 작업을 줄일 수 있다.
|
|
|
|
대신 Role을 Header로 전달하기 위한 계약을 먼저 정해야 한다.
|
|
사용자가 여러 Role을 가질 때 어떤 형식으로 직렬화할지, Header에 허용할 최대 크기를 어디까지로 할지, 사용자의 Role이 변경되었을 때 언제부터 새로운 값이 요청에 반영되는지도 명확하게 정의해야 한다.
|
|
|
|
또한 Upstream은 전달받은 Role이 원래 인증 시스템의 값과 일치하는지 확인할 수 있어야 하고, 그러지 못하면 Edge가 전달한 값을 그대로 신뢰하게 된다. 따라서 Edge에서 Role을 잘못 계산하거나 오래된 값을 전달하면 Upstream의 인가 판단도 그대로 잘못될 수 있다.
|
|
|
|
이 구조를 선택하게 되면 Edge가 Role 정보를 만드는 과정과 Header를 전달하는 경로를 신뢰 경계의 일부로 보고,
|
|
Role 갱신과 전달 오류를 어떻게 검증할지도 함께 설계해야 한다.
|
|
|
|
### 3. tenant와 인가 판단까지 edge에 둔다
|
|
|
|
Tenant 정보는 단순한 사용자 속성이 아니라 어떤 조직의 데이터에 접근할 수 있는지를 결정하는 값이다.
|
|
따라서 잘못된 Tenant 값 하나가 전달되면 다른 조직의 데이터에 접근하는 문제로 바로 이어질 수 있다.
|
|
|
|
이 때문에 Tenant를 Edge Header로 전달하려면 Upstream에서도 해당 사용자가 실제로 그 Tenant에 속하는지 다시 확인할 수 있는 방법이 필요하다.
|
|
현재 구성에는 이러한 재검증 수단이 없으므로 Tenant까지 Edge가 책임지는 구조로 확장하기에는 위험이 크다.
|
|
|
|
더 나아가 Tenant나 Role을 이용한 실제 인가 판단까지 Edge로 옮기면 Edge가 애플리케이션의 도메인 규칙을 알아야 한다.
|
|
어떤 사용자가 어떤 조직의 어떤 기능을 사용할 수 있는지 같은 정책이 바뀔 때마다 Edge의 로직도 함께 수정하고 배포해야 한다.
|
|
|
|
따라서 Edge는 인증된 사용자 정보를 전달하는 역할에 가깝게 유지하고, Tenant 소속 관계나 도메인에 종속된 인가 규칙은 Upstream 애플리케이션에서 검증하는 구조를 우선 검토한다.
|
|
|
|
### 4. 헤더 계약 대신 BFF가 인가와 API 호출을 맡는다
|
|
|
|
Role이나 Tenant 같은 정보를 Edge Header에 계속 추가하는 대신, BFF가 사용자에게 필요한 정보를 직접 조회하고 인가 판단과 API 호출을 처리하는 구조도 선택할 수 있다.
|
|
|
|
이 구조에서는 Edge가 애플리케이션의 Role, Tenant, 권한 정책까지 알 필요가 없다.
|
|
BFF가 필요한 사용자와 권한 정보를 조회해 인가를 판단하고, 화면에 필요한 여러 Resource Server의 API를 호출해 결과를 조합할 수 있다. 따라서 애플리케이션 도메인에 가까운 책임을 Edge Header 계약에서 분리할 수 있다.
|
|
|
|
이 구조에서는 BFF를 도입하면서 서버가 다시 인증 상태를 관리해야 한다.
|
|
브라우저와 BFF 사이의 Application Session을 보호해야 하고, Cookie 기반 Session을 사용한다면 상태 변경 요청에 대한 CSRF 검증도 필요하다.
|
|
여러 BFF Replica에서 인증 상태를 유지해야 한다면 Session과 Authorized Client를 어떻게 공유할지 결정하고 Shared Store의 장애와 만료 처리도 운영해야 한다.
|
|
|
|
## 다음 검증
|
|
|
|
upstream이 실제로 요구하는 claim을 먼저 적는다.
|
|
|
|
1. 전달하려는 claim이 계속 늘어나는가.
|
|
2. role이나 tenant 변경이 즉시 반영돼야 하는가.
|
|
3. 정책이 애플리케이션 도메인을 알아야 하는가.
|
|
4. 헤더 값이 인가 판단의 근거가 되는가.
|
|
5. 서비스별 정책 차이가 커지는가.
|
|
|
|
2번부터 5번 중 하나라도 그렇다면 헤더를 늘리는 대신 BFF 구조를 검토한다.
|
|
|
|
role을 헤더에 담는 구성을 먼저 만들고, 다중 값과 크기 상한을 넣어 무엇이 먼저 잘못되는지 확인한다.
|
|
role을 바꾼 뒤 몇 번째 요청부터 반영되는지도 확인한다.
|