Files
document-haness/docs/keycloak-session-store/final/.techviz/b4-header-trust-boundary/spec.json
T
DongHyeonkaandClaude Opus 5 75bed382c8 docs(keycloak-session-store): remake all 28 diagrams through the techviz pipeline
The originating repository's SVGs were drawn by hand and every one of them
put a title, a subtitle and an explanation band inside the canvas. This
repository forbids both, so they could not be carried over — the whole set
was rebuilt through the skill's pipeline instead.

Each diagram went through prepare, references, prompt, a VizSpec 1.1 citing
document line ranges, lint, and render. All 28 pass lint and produce the
same eight formats the existing keycloak project has. Sentences moved out of
the canvas into <desc> and the paragraph beside each figure; the drawings
carry names only.

Two lint rules did real work rather than formatting work:

  edge-through-node                  caught arrows crossing an unrelated
                                     node and implying an adjacency that
                                     does not exist — four diagrams had to
                                     be restructured, not just relaid out
  evidence-outside-prepared-context  caught a diagram citing another
                                     section; its anchor moved from B-0 to
                                     B-1 so all three sections it draws on
                                     are inside the prepared context

lab-topology also had to change profile: its context offers a different
candidate set, and query-fanout with shard roles is what the section
actually shows — one entry point spreading to two Keycloak nodes.

The document now carries all 28 inline, one per claim that needed one, and
the section recording what was still missing is updated: the diagram gap is
closed, Studio records remain.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 11:03:10 +09:00

154 lines
4.3 KiB
JSON

{
"version": "1.1",
"id": "b4-header-trust-boundary",
"title": "지우지 않으면 통과한다",
"question": "Edge 가 넣어주는 인가 헤더를 앱이 믿어도 되는가",
"type": "architecture",
"direction": "TB",
"audience": [
"Forward-Auth 구조를 운영하는 엔지니어"
],
"summary": "nginx 는 자기가 설정하지 않은 동명 헤더를 덮어쓰지 않는다. 먼저 지워야 한다.",
"alt": "밖에서 들어온 위조 헤더가 프록시를 그대로 통과해 앱에 닿는 구성. 프록시가 그 이름을 설정할 때만 덮어쓴다.",
"long_description": "nginx 는 자기가 proxy_set_header 로 설정한 헤더만 덮어쓴다. 설정하지 않은 이름은 클라이언트가 보낸 값이 그대로 지나간다. 그래서 X-Auth-Request-Roles 같은 헤더를 앱이 믿으려면 프록시가 그 이름을 먼저 빈 값으로 지워야 한다. 그리고 IdP 에서 클레임을 바꿔도 반영되지 않는다. 12회 요청 6초 동안 옛 값이 갔고 세션을 지워 재인증한 뒤에야 새 값이 왔다. 세션은 로그인 시점의 스냅샷이다.",
"source_context": {
"document": "docs/keycloak-session-store/final/document.md",
"document_sha256": "1d44cba1905544d92f1d26ae36a8deb64a3db3914d6b488fd30d6ae7f8cfbabe",
"anchor": {
"kind": "heading",
"value": "B-4 · Edge 인가의 범위 (Q4)",
"line": 401
}
},
"composition": {
"profile": "component-flow",
"diagram_only": true,
"reference_ids": [
"payment-event-flow"
],
"rationale": "헤더가 어디서 설정되고 어디서 신뢰되는가가 지배적 질문이다. 신뢰 경계이므로 component-flow 를 골랐다."
},
"groups": [],
"nodes": [
{
"id": "attacker",
"label": "밖에서 보낸 위조 헤더",
"kind": "actor",
"role": "source",
"emphasis": "warning",
"description": "앱이 믿는 이름을 그대로 쓴다.",
"details": [
"X-Auth-Request-Roles"
],
"evidence": [
{
"start_line": 393,
"end_line": 400
}
],
"assumption": false
},
{
"id": "nginx",
"label": "nginx",
"kind": "gateway",
"role": "control",
"emphasis": "warning",
"description": "설정하지 않은 이름은 덮어쓰지 않는다.",
"details": [
"proxy_set_header ... \"\" 로 먼저 지워야 한다"
],
"evidence": [
{
"start_line": 393,
"end_line": 400
}
],
"assumption": false
},
{
"id": "proxy",
"label": "oauth2-proxy",
"kind": "gateway",
"role": "control",
"emphasis": "normal",
"description": "인증 결과를 헤더로 넣는다.",
"details": [
"--set-xauthrequest"
],
"evidence": [
{
"start_line": 393,
"end_line": 400
}
],
"assumption": false
},
{
"id": "app",
"label": "앱",
"kind": "service",
"role": "target",
"emphasis": "primary",
"description": "헤더를 믿고 인가한다.",
"details": [
"세션은 로그인 시점의 스냅샷"
],
"evidence": [
{
"start_line": 401,
"end_line": 408
}
],
"assumption": false
}
],
"edges": [
{
"id": "a-n",
"from": "attacker",
"to": "nginx",
"label": "위조 헤더",
"kind": "request",
"evidence": [
{
"start_line": 393,
"end_line": 400
}
],
"assumption": false
},
{
"id": "n-p",
"from": "nginx",
"to": "proxy",
"label": "지우지 않으면 그대로 지난다",
"kind": "request",
"evidence": [
{
"start_line": 393,
"end_line": 400
}
],
"assumption": false
},
{
"id": "p-a",
"from": "proxy",
"to": "app",
"label": "인가 헤더",
"kind": "request",
"evidence": [
{
"start_line": 393,
"end_line": 408
}
],
"assumption": false
}
],
"legend": [],
"metadata": {
"rationale": "위조 경로와 정상 경로가 같은 헤더 이름을 쓴다는 것을 그렸다. 지우는 단계가 없으면 둘을 구별할 수 없다."
}
}