Files
Gnuboard7/tests/scenarios/trusted-proxy.yaml
T
HeuJung b6c5e1f323 feat(core): 리버스 프록시 신뢰 설정 지원 및 미설정 진단
TLS 가 앞단에서 종단되고 앱에는 HTTP 로 전달되는 구성에서 X-Forwarded-* 가 전부
무시되어 화면 백지·IP 왜곡·webhook 403 이 함께 발생했다. 코어에 신뢰 프록시 설정
지점이 아예 없던 것이 원인이다.

- config/trustedproxy.php 로 내장 TrustProxies 미들웨어에 값을 공급한다.
 bootstrap/app.php 는 건드리지 않는다 — withMiddleware 클로저는 .env 로드 전에
 평가되어 env 가 항상 null 이 되는 조용한 no-op 이다.
- 판정은 App\Support\TrustedProxyDiagnostic 단일 SSoT 에서 계산하고 대시보드
 알림·환경설정 고급 탭·설치 마법사·trusted-proxy:status 네 면이 소비한다.
 판정식은 "HTTPS 인식 실패" 가 아니라 "X-Forwarded-* 수신 중 AND 신뢰 프록시
 미설정" 이다 — HTTP 전용 사이트가 프록시 뒤에 있으면 화면은 정상인 채로
 나머지만 조용히 어긋나기 때문이다.
- 값 편집 UI 와 쓰기 엔드포인트는 두지 않는다(잠금 역설 + XFF 위조 경로).
- 혼합 콘텐츠 차단을 부트스트랩 폴백의 별도 사유로 가른다. 새로고침으로 낫지
 않으므로 버튼을 렌더하지 않고, 원인·조치는 콘솔로 운영자에게 보낸다.
- 대시보드 알림을 심각도로 배치한다 — warning 은 상단 배너, 그 외는 하단 카드.
 같은 알림이 두 곳에 뜨지 않으며, 여러 건은 간격을 두고 쌓인다.

공개 이슈: (@lyg-kaban 제보)
2026-08-28 18:09:27 +09:00

96 lines
6.0 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# audit:allow test-scenario-coverage reason: 본 매니페스트는 신규 기능 시나리오 매트릭스 SSoT 로 기록. 축 곱(6×3×3×3×2×2 = 648)이 커 전 조합 docblock 매핑은 비현실적이며, 핵심 회귀 가드는 test_files 의 통과 테스트로 커버.
feature: 리버스 프록시 신뢰 설정 (Trusted Proxy)
description: |
TLS 가 앞단에서 종단되고 앱에는 HTTP 로 전달되는 구성(AWS ALB, CloudFront, Cloudflare,
nginx/Apache 리버스 프록시, ngrok)에서 요청 객체가 접속 주소와 방문자 IP 를 올바르게
인식하도록 신뢰할 프록시를 지정한다 (공개 #124).
배경:
- 코어에 신뢰 프록시 설정이 전혀 없어, Laravel 내장 TrustProxies 미들웨어가
X-Forwarded-* 를 전부 무시했다. 그 결과 요청이 평문 HTTP 로 인식되어
asset()/url() 이 http:// 절대 URL 을 만들고, HTTPS 페이지에서 혼합 콘텐츠로
차단되어 사이트 전체가 백지가 되었다.
- 같은 원인에서 통보 IP 화이트리스트 403, 로그인 시도 제한 붕괴, IP 기록 왜곡,
쿠키 Secure 누락, SEO canonical 다운그레이드, GeoIP 오판정이 함께 갈라진다.
조치:
- config/trustedproxy.php 1개 (`'proxies' => env('TRUSTED_PROXIES')`).
코어 PHP 코드 수정 0줄 — 내장 미들웨어가 요청 시점에 이 키를 폴백으로 읽는다.
- bootstrap/app.php 는 건드리지 않는다. withMiddleware 클로저는 .env 로드 전에
평가되어 env() 가 항상 null 이므로 그 경로는 조용한 no-op 이 된다.
- 미설정이 기본값이므로 기존 설치처 동작은 바뀌지 않는다(opt-in).
진단 (읽기 전용, 4면):
- 판정식은 "HTTPS 인식 실패" 가 아니라
`X-Forwarded-* 수신 중 AND 신뢰 프록시 미설정` 이다. HTTP 전용 사이트가 프록시
뒤에 있으면 화면은 완전히 정상 렌더되면서 나머지 파손만 조용히 계속되기 때문이다.
- 판정은 App\Support\TrustedProxyDiagnostic 한 곳에서만 계산하고
대시보드 알림 · 환경설정 고급 탭 · 설치 마법사 · Artisan 커맨드가 소비한다.
- 값 편집 UI 는 두지 않는다 — 잠금 역설(프록시 뒤에서는 그 화면 자체가 뜨지 않는다)
과 권한 승격(관리자 계정 탈취가 곧 XFF 위조 경로)이 이유다.
axes:
proxies_setting: [unset, star, double_star, single_ip_match, single_ip_mismatch, cidr]
forwarded_proto: [https, http, absent]
forwarded_for: [single, chain, absent]
forwarded_host: [same, different, absent]
forwarded_port: [nonstandard, absent]
session_secure_cookie: [unset, true]
exclusions:
- { proxies_setting: unset, forwarded_proto: https, reason: "미설정은 헤더를 전부 무시 — 헤더 조합이 결과를 바꾸지 않는다" }
- { proxies_setting: unset, forwarded_proto: http, reason: "동일 사유" }
- { proxies_setting: unset, forwarded_for: chain, reason: "동일 사유" }
- { proxies_setting: unset, forwarded_host: different, reason: "동일 사유" }
- { proxies_setting: unset, forwarded_port: nonstandard, reason: "동일 사유" }
- { proxies_setting: single_ip_mismatch, forwarded_proto: https, reason: "목록 불일치는 미설정과 동일 경로 — 헤더 조합이 결과를 바꾸지 않는다" }
- { proxies_setting: single_ip_mismatch, forwarded_for: chain, reason: "동일 사유" }
- { proxies_setting: single_ip_mismatch, forwarded_host: different, reason: "동일 사유" }
- { forwarded_proto: absent, session_secure_cookie: unset, reason: "스킴 헤더가 없으면 쿠키 Secure 자동 판정 축이 성립하지 않는다" }
- { session_secure_cookie: true, forwarded_proto: http, reason: "명시 설정은 스킴 판정을 우회하므로 조합이 무의미하다" }
- { session_secure_cookie: true, forwarded_proto: absent, reason: "동일 사유" }
effects:
- unset_ignores_forwarded_headers
- star_restores_scheme_ip_and_root
- double_star_behaves_like_star
- chain_requires_listing_every_hop
- ip_list_match_trusts_and_mismatch_rejects
- cidr_notation_is_honored
- config_file_exposes_proxies_key_from_env
- bootstrap_does_not_reintroduce_env_trust_proxies
- env_examples_document_trusted_proxies
- installer_header_list_matches_core_diagnostic
- cookie_secure_follows_trusted_scheme
- direct_exposure_is_not_a_warning
- forwarded_without_config_is_warning
- http_only_behind_proxy_still_warns
- configured_proxy_is_ok
- empty_string_counts_as_unset
- console_context_is_not_applicable
- dashboard_alert_only_on_warning_with_warning_type
- diagnostic_endpoint_is_read_only
test_files:
- tests/Feature/Http/TrustedProxyContractTest.php
- tests/Feature/Http/TrustedProxyDiagnosticTest.php
notes: |
`**` 는 Laravel 내장 TrustProxies 에서 `*` 과 **같은 코드 경로**를 탄다
(`setTrustedProxyIpAddressesToTheCallingIp`). 별도 패키지 시절의 "모든 프록시 신뢰"
의미가 아니므로, 다단계 체인에서는 둘 다 마지막 프록시를 방문자로 본다. 실측으로
확인했고 `double_star_behaves_like_star` 가 그 사실을 계약으로 잠근다 — 프레임워크
업그레이드로 의미가 갈라지면 문서·설정 주석과 함께 갱신해야 한다.
프록시를 실제로 경유하는 브라우저 축(혼합 콘텐츠 차단 → 화면 백지)은 이 매니페스트
범위 밖이다. Playwright 에 TLS 종단 프록시 픽스처를 두면 인증서·포트·globalSetup 이
기존 E2E 의 단일 origin 전제를 깨뜨리므로, 그 축은 Chrome MCP 수동 매트릭스가 덮는다.
부팅 안내의 `blocked` 분기는 프록시 없이 재현 가능하므로
`tests/scenarios/bootstrap-parse-failure.yaml` 이 별도로 다룬다.
이미 발송된 서명 URL 무효화 · 과거 적재 IP · SEO 캐시/사이트맵 재생성은 설정을 켜도
자동으로 낫지 않는 1회성 후속 조치다. 코드가 아니라 운영 절차이므로 축에 넣지 않고
docs/backend/reverse-proxy.md 가 문서로 고정한다.