Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/duplicate-mechanisms/case/case-trust-policy-lives-in-nginx-not-in-the-code.md
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
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>
2026-09-04 22:51:59 +09:00

5.2 KiB

kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, assets, evidence, source
kind slug title topic project status sourceRevision rootTreeNode evidenceCapturedOn assets evidence source
CASE trust-policy-lives-in-nginx-not-in-the-code forwarded 헤더 신뢰 판정이 Nginx에만 있고 Java 정책 421 LOC은 배선되지 않았다 duplicate-mechanisms clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 case:trust-policy-lives-in-nginx-not-in-the-code 2026-09-01
key file
trust-policy-lives-in-nginx-not-in-the-code ../../../final/evidence/rendered/trust-policy-lives-in-nginx-not-in-the-code.svg
../../../final/evidence/raw/trust-policy-lives-in-nginx-not-in-the-code.txt
원본 분석 절은 final/document.md#3-1 · analysis/14 §32.2 이다.

forwarded 헤더 신뢰 판정이 Nginx에만 있고 Java 정책 421 LOC은 배선되지 않았다

웹 리프의 프록시 패키지는 421 줄로 신뢰 프록시 정책과 헤더 정화기와 정규화 타입을 갖는다. 실제 신뢰 판정은 Nginx 설정이 하고, 그 설정은 들어온 forwarded 헤더를 원격 주소로 교체한다.

관계

  • 중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다 이 사례가 그 규칙의 인프라 판이다.
  • 요청 식별자를 클라이언트가 고를 수 없다는 정책이 다른 필터에서 뒤집힌다 같은 리프에서 같은 계열의 사례다.

문제

웹 리프의 프록시 패키지는 네 파일로 421 줄이다.

TrustedProxyPolicy 161 줄 NormalizedForwardedHeaders 158 줄 ForwardedHeaderSanitizer 72 줄 UntrustedForwardedHeaderException 30 줄

이 코드가 하는 일은 어떤 프록시를 신뢰할지 정하고 forwarded 헤더를 정규화하는 것이다.

문제는 이것이 실제 판정 경로인가다.

결론

Nginx 설정이 그 판정을 대신한다.

프록시 헤더 설정 파일의 주석이 자기 지위를 명시한다. 이것이 권위 있는 forwarded 헤더이며 모든 location 에서 include 된다는 것이다.

설정 내용은 교체다.

Host 를 host 변수로 설정 X-Real-IP 를 remote_addr 로 설정 X-Forwarded-For 를 remote_addr 로 설정 X-Forwarded-Host 를 이 배포의 공개 이름으로 설정

주석이 모든 줄이 SET 이고 ADD 가 아니라고 못 박는다. 클라이언트가 보낸 X-Forwarded-For 는 remote_addr 로 교체되고 X-Forwarded-Host 는 이 배포의 공개 이름으로 교체된다.

즉 애플리케이션에 도달하는 시점에 그 헤더들은 이미 신뢰할 수 있는 값이다. Java 정책이 판정할 것이 남아 있지 않다.

Java 쪽 참조 수도 그것과 맞는다.

ForwardedHeaderSanitizer : main 참조 0 TrustedProxyPolicy : main 참조 1, test 참조 2 UntrustedForwardedHeaderException : main 참조 1, test 참조 1 NormalizedForwardedHeaders : main 참조 2

같은 설정 파일의 주석이 왜 include 방식인지도 적는다. Nginx 의 배열 지시어 상속 규칙이 병합이 아니라 교체이기 때문이다. location 안의 proxy_set_header 하나가 server 수준에서 상속된 모든 proxy_set_header 를 버린다. 보안 헤더를 server 수준에 두고 location 마다 하나씩 추가하는 설정은 보안 헤더를 하나도 보내지 않으며, 유일한 증상은 애플리케이션이 조용히 클라이언트를 다시 신뢰하는 것이다.

그 주석이 이 사례의 위험을 정확히 서술한다. 신뢰 판정이 인프라에 있으면 인프라 설정 실수가 애플리케이션의 신뢰 정책을 조용히 되돌린다. 그리고 그때 되돌아갈 Java 정책은 배선되어 있지 않다.

검증 환경

Nginx 설정 : 웹 리프의 nginxProxyTest 소스셋 아래 proxy_headers.conf 확인 방식 : 파일 LOC 계수와 타입별 참조 계수, 설정 파일 대조 소스 수정 : x

재현 조건

  1. 웹 리프의 proxy 패키지 파일과 줄 수를 센다. 네 파일 421 줄이다.
  2. 각 타입의 main 참조와 test 참조를 센다.
  3. Nginx 프록시 헤더 설정을 읽는다. 모든 지시어가 SET 이다.
  4. 그 설정의 주석에서 include 방식을 택한 이유를 읽는다.

본문

forwarded 헤더를 어디까지 믿을지 판정하는 Java 정책이 421 LOC 작성돼 있고 배선되지 않는다. 실제 판정은 Nginx 설정이 한다.

판정을 실제로 하는 곳

:::evidence key="trust-policy-lives-in-nginx-not-in-the-code" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true" :::

리뷰가 닿지 않는 자리로 정책이 옮겨졌다

두 곳이 어긋나면 코드 리뷰가 잡을 수 없고, Java 쪽을 고쳐도 동작이 바뀌지 않는다.

확인하지 못한 것

이 Nginx 설정이 실제 배포에서 쓰이는 설정과 같은지 확인하지 않았다. 확인한 파일은 웹 리프의 테스트 소스셋 아래에 있다. 별도로 인프라 디렉터리에 파일서버용 Nginx 설정이 있고 그것은 다른 파일이다.

Java 정책이 어떤 경로에서 호출되는지 그 한 건씩의 참조를 추적하지 않았다.