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 제보)
This commit is contained in:
1 parent
46ada4dddb
commit
b6c5e1f323
55 files changed
+2861
-38
No files matched your search
@@ -200,7 +200,7 @@ location ~* \.(js|css|json)$ { expires max; access_log off; }
|
||||
## 코어 API 레퍼런스
|
||||
|
||||
<!-- @generated:start:api-readme-index -->
|
||||
- **문서 수**: 36 · **엔드포인트 수**: 324
|
||||
- **문서 수**: 36 · **엔드포인트 수**: 325
|
||||
|
||||
| 문서 | 도메인 | 엔드포인트 |
|
||||
| --- | --- | --- |
|
||||
@@ -235,7 +235,7 @@ location ~* \.(js|css|json)$ { expires max; access_log off; }
|
||||
| [schedules.md](schedules.md) | `schedules` | 12 |
|
||||
| [search.md](search.md) | `search` | 1 |
|
||||
| [seo.md](seo.md) | `seo` | 5 |
|
||||
| [settings.md](settings.md) | `settings` | 15 |
|
||||
| [settings.md](settings.md) | `settings` | 16 |
|
||||
| [system.md](system.md) | `system` | 2 |
|
||||
| [templates.md](templates.md) | `templates` | 57 |
|
||||
| [users.md](users.md) | `users` | 12 |
|
||||
|
||||
@@ -141,13 +141,13 @@ Authorization: Bearer {YOUR_TOKEN}
|
||||
|
||||
**응답 필드** (`data` 내부)
|
||||
|
||||
_목록 응답: `data` 배열 항목의 필드. `data` 는 `core.dashboard.alerts` 필터 훅의 결과이며, 알릴 항목이 없으면 빈 배열(`[]`)입니다. 코어 기본 리스너(`ExtensionCompatibilityAlertListener`, `StaticPublishFailureAlertListener`)가 주입하는 항목의 필드는 다음과 같습니다._
|
||||
_목록 응답: `data` 배열 항목의 필드. `data` 는 `core.dashboard.alerts` 필터 훅의 결과이며, 알릴 항목이 없으면 빈 배열(`[]`)입니다. 코어 기본 리스너(`ExtensionCompatibilityAlertListener`, `StaticPublishFailureAlertListener`, `TrustedProxyAlertListener`)가 주입하는 항목의 필드는 다음과 같습니다._
|
||||
|
||||
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | string | `compat_plugins_sirsoft-gdpr` | 알림 식별자 (`compat_{type}_{identifier}` = 자동 비활성화, `recover_{type}_{identifier}` = 재호환. 알림 닫기(dismiss) 상태 판정 키) |
|
||||
| type | string | `warning` | 알림 등급 (`warning`: 코어 비호환 자동 비활성화, `info`: 재호환 복구 가능) |
|
||||
| subtype | string | `incompatible_core` | 알림 세부 분류 (`incompatible_core`: 코어 버전 비호환으로 자동 비활성화됨, `recovery_available`: 코어 업그레이드 후 다시 활성화 가능, `static_publish_parent_not_writable` · `static_publish_write_failed` · `static_publish_lock_unavailable`: 초기 화면 파일 생성이 2회 이상 연속 실패 — 각각 폴더 권한 / 디스크 공간 / 캐시 저장소가 원인) |
|
||||
| type | string | `warning` | 알림 등급. **화면 배치를 결정합니다** — `warning` 은 관리자 대시보드 **상단 배너**로, 그 외(`info` 등)는 하단 「시스템 알림」 카드로 렌더됩니다. 같은 알림이 두 곳에 중복 노출되지 않습니다 |
|
||||
| subtype | string | `incompatible_core` | 알림 세부 분류 (`incompatible_core`: 코어 버전 비호환으로 자동 비활성화됨, `recovery_available`: 코어 업그레이드 후 다시 활성화 가능, `static_publish_parent_not_writable` · `static_publish_write_failed` · `static_publish_lock_unavailable`: 초기 화면 파일 생성이 2회 이상 연속 실패 — 각각 폴더 권한 / 디스크 공간 / 캐시 저장소가 원인, `trusted_proxy_missing`: 리버스 프록시 헤더를 수신 중인데 신뢰 프록시가 설정되지 않음) |
|
||||
| icon | string | `exclamation-triangle` | 아이콘 식별자 (warning: `exclamation-triangle`, info: `check-circle`) |
|
||||
| title | string | `플러그인 "sirsoft-gdpr" 자동 비활성화됨` | 알림 제목 (다국어 문구 — `extensions.alerts.incompatible_deactivated` / `recovered_title`) |
|
||||
| message | string | `필요 버전: 7.0.0-beta.9, 현재 설치됨: 7.0.0-beta.8` | 알림 본문 (다국어 문구 — `extensions.alerts.incompatible_message` / `recovered_body`) |
|
||||
|
||||
@@ -1124,6 +1124,82 @@ HTTP/1.1 200
|
||||
서버 실행 환경 정보를 한 번에 조회합니다. OS/웹서버/PHP/DB/Laravel/코어 버전, CPU·메모리·디스크 사용량, PHP 주요 설정값(memory_limit·max_execution_time·upload_max_filesize), 주요 경로, PHP 확장 로드 상태, DB 연결 구성 요약 등을 포함합니다. 관리자 시스템 정보 화면과 요구사항 점검용 진단 데이터로 사용됩니다.
|
||||
|
||||
|
||||
### GET /api/admin/settings/trusted-proxy
|
||||
<!-- @generated:start:api.admin.settings.trusted-proxy -->
|
||||
- **라우트명**: `api.admin.settings.trusted-proxy`
|
||||
- **컨트롤러**: `App\Http\Controllers\Api\Admin\SettingsController@trustedProxy`
|
||||
- **인증/권한**: `auth:sanctum` + `permission:core.settings.read`
|
||||
|
||||
**요청 파라미터**
|
||||
|
||||
_요청 파라미터 없음._
|
||||
|
||||
**요청 예시**
|
||||
|
||||
```http
|
||||
GET /api/admin/settings/trusted-proxy HTTP/1.1
|
||||
Host: api.example.com
|
||||
Accept: application/json
|
||||
Authorization: Bearer {YOUR_TOKEN}
|
||||
```
|
||||
|
||||
**응답 필드** (`data` 내부)
|
||||
|
||||
_단건 응답: `data` 객체의 필드._
|
||||
|
||||
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
|
||||
| --- | --- | --- | --- |
|
||||
| forwarded_headers | array | `["X-Forwarded-For","X-Forwarded-Proto"]` | 이 요청이 수신 중인 `X-Forwarded-*` 계열 헤더 이름 목록. 비어 있으면 앞단에 프록시가 없는 직접 노출 구성이다 |
|
||||
| trusted_configured | boolean | `false` | `TRUSTED_PROXIES` 가 지정되어 있는지. 빈 문자열은 미설정과 같게 판정된다 |
|
||||
| configured_proxies | string\|null | `null` | 지정된 신뢰 프록시 값 (미설정이면 `null`) |
|
||||
| is_secure | boolean\|null | `false` | 요청이 HTTPS 로 인식되었는지. 요청이 없는 맥락에서는 `null` |
|
||||
| client_ip | string\|null | `10.0.0.5` | 방문자 IP 로 인식된 값. 신뢰 프록시가 없으면 프록시 자신의 주소가 된다 |
|
||||
| remote_addr | string\|null | `10.0.0.5` | 직전 호출 IP(`REMOTE_ADDR`). `client_ip` 와 같으면서 `forwarded_headers` 가 비어 있지 않으면 모든 방문자가 한 사람으로 기록되고 있는 상태다 |
|
||||
| status | string | `warning` | 진단 결과. `warning`(프록시 헤더 수신 중 + 신뢰 프록시 미설정) / `ok` / `not_applicable`(요청이 없는 맥락) |
|
||||
|
||||
**응답 예시**
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "성공적으로 처리되었습니다.",
|
||||
"data": {
|
||||
"forwarded_headers": [
|
||||
"X-Forwarded-For",
|
||||
"X-Forwarded-Proto"
|
||||
],
|
||||
"trusted_configured": false,
|
||||
"configured_proxies": null,
|
||||
"is_secure": false,
|
||||
"client_ip": "10.0.0.5",
|
||||
"remote_addr": "10.0.0.5",
|
||||
"status": "warning"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**에러 응답**
|
||||
|
||||
| 상태코드 | 의미 | 발생 조건 |
|
||||
| --- | --- | --- |
|
||||
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
|
||||
| 403 | Forbidden | 요구 권한(`core.settings.read`)이 없는 경우 |
|
||||
|
||||
<!-- @generated:end -->
|
||||
|
||||
**설명**
|
||||
|
||||
리버스 프록시 뒤에서 구동 중인지, 그리고 신뢰할 프록시가 지정되어 있는지를 진단합니다. **읽기 전용이며 대응하는 쓰기 엔드포인트가 없습니다** — 값은 `.env` 의 `TRUSTED_PROXIES` 로만 변경합니다.
|
||||
|
||||
판정식은 "HTTPS 인식 실패" 가 아니라 `forwarded_headers 가 비어 있지 않음 AND trusted_configured 가 거짓` 입니다. HTTP 전용 사이트가 프록시 뒤에 있는 구성에서는 혼합 콘텐츠 차단이 없어 화면이 정상으로 보이지만, 방문자 IP 기록·통보 IP 화이트리스트·로그인 시도 제한은 그대로 어긋나기 때문입니다.
|
||||
|
||||
같은 판정을 관리자 대시보드 알림, 환경설정 > 고급 화면, 설치 마법사, `php artisan trusted-proxy:status` 가 공유합니다. 설정 방법과 도입 시 후속 조치는 [reverse-proxy.md](../reverse-proxy.md) 를 참고하세요.
|
||||
|
||||
|
||||
### POST /api/admin/settings/test-driver
|
||||
<!-- @generated:start:api.admin.settings.test-driver -->
|
||||
- **라우트명**: `api.admin.settings.test-driver`
|
||||
|
||||
Reference in new issue
Block a user