Files
mc_domain_proxy/README.md
claude-bot 58b112e449 feat: per-domain backend routing
Each allowed_domains entry can now carry its own backend {host, port}.
That lets one proxy on port 25565 serve multiple MC servers, picking
the upstream from the domain the client typed.

- proxy/main.py: ProxyState.backend_for(domain) → tuple|None,
  honors per-domain backend first, falls back to top-level backend.
  handle_client uses backend_for(); blocked / disabled domains
  return None (and still get a Login Disconnect on join attempts).
- api/routes/{config,domains}.py: DomainBackend model + optional
  backend field on create/patch. PATCH supports clear_backend=true
  to drop a per-domain override and revert to default.
- frontend/Domains.jsx: full rewrite — new-domain form has host/port
  inputs, table shows each row's effective backend, inline edit +
  reset button per row.
- frontend/Settings.jsx: backend section relabeled "기본 백엔드 (fallback)"
- README updated with multi-server example config.
2026-05-23 17:25:14 +09:00

185 lines
7.6 KiB
Markdown

# MC Domain Filter Proxy
마인크래프트 서버 앞단에 두는 **도메인 화이트리스트 프록시 + 웹 관리 대시보드**.
클라이언트가 마인크래프트 서버 주소창에 입력한 도메인이 허용 목록과 일치할 때만 백엔드 MC 서버로 연결을 통과시킵니다. 외부에서 공유기의 공인 IP를 직접 입력해서 접속하는 경로를 차단할 수 있습니다.
## 기능
- 마인크래프트 핸드셰이크 패킷에서 클라이언트가 입력한 server address 파싱
- 허용 도메인 화이트리스트 매칭, 불일치 시 Login Disconnect 패킷으로 차단 사유(커스텀 가능) 표시 후 연결 종료
- 통과한 연결은 백엔드 MC 서버로 투명 TCP 중계
(Fabric / Paper / Spigot / NeoForge 등 서버 종류 무관)
- **도메인별 백엔드 라우팅**: 도메인마다 다른 IP:포트로 보낼 수 있어 하나의 25565 포트로 여러 MC 서버를 동시에 운영 가능 (예: `mc.tkrmagid.kr` → 게임PC:25565, `creative.tkrmagid.kr` → NAS:25566)
- 설정 파일(`data/config.json`) 변경을 프록시가 자동 감지해 hot reload (재시작 불필요)
- 모든 연결 시도(허용 / 차단 / 에러)를 SQLite 에 기록
- 웹 대시보드 (NPM 스타일): 도메인 관리, 실시간 로그, 통계 카드, 백엔드/포트 설정
## 네트워크 구조
```
외부 인터넷
공유기 (포트포워딩 25565 → Proxmox)
mc-filter-proxy 컨테이너 (25565)
게임PC 내부IP:25565 (실제 마인크래프트 서버)
```
## 빠른 시작
1. 저장소 클론
```bash
git clone https://git.tkrmagid.kr/tkrmagid/mc_domain_proxy.git
cd mc_domain_proxy
```
2. (선택) 초기 설정 파일을 미리 생성. 생략하면 프록시 첫 기동 시 기본값으로 자동 생성됩니다.
```bash
mkdir -p data
cat > data/config.json <<'EOF'
{
"proxy": { "listen_port": 25565, "enabled": true },
"backend": { "host": "192.168.0.20", "port": 25565 },
"block_message": "이 서버는 허용된 도메인에서만 접속 가능합니다.",
"allowed_domains": [
{ "domain": "mc.tkrmagid.kr", "enabled": true, "note": "메인 서버" },
{ "domain": "creative.tkrmagid.kr", "enabled": true, "note": "크리에이티브",
"backend": { "host": "192.168.0.21", "port": 25566 } }
]
}
EOF
```
각 도메인 entry 에 `backend` 필드가 있으면 그 host:port 로, 없으면 top-level `backend` 로 라우팅됩니다.
3. 전체 스택 빌드 & 실행
```bash
docker compose up -d --build
```
4. 접속
- **마인크래프트 클라이언트**: `mc.tkrmagid.kr` (또는 등록한 도메인)
- **대시보드**: `http://<Proxmox-IP>:8080`
## 구성 요소
| 서비스 | 포트 | 설명 |
|-------------|-----------------|----------------------------------------|
| `proxy` | 25565 (외부) | asyncio TCP 프록시, 핸드셰이크 파싱 |
| `api` | 8000 (내부) | FastAPI, 설정/로그/통계 REST API |
| `frontend` | 3000 (내부) | React + Vite SPA |
| `nginx` | 8080 (외부) | 대시보드 리버스 프록시 |
`data/` 디렉터리는 모든 서비스가 공유 볼륨으로 마운트해 `config.json`, `logs.db` 를 공용합니다.
## API
| 메서드 | 경로 | 설명 |
|--------|---------------------------------|----------------------------------------|
| GET | `/api/config` | 전체 설정 조회 |
| PUT | `/api/config` | 전체 설정 저장 |
| GET | `/api/domains` | 허용 도메인 목록 |
| POST | `/api/domains` | 도메인 추가 |
| PATCH | `/api/domains/{domain}` | 활성/메모 변경 |
| DELETE | `/api/domains/{domain}` | 도메인 삭제 |
| GET | `/api/logs?limit&offset&action` | 접속 로그 (페이지네이션) |
| GET | `/api/status` | 프록시 상태 + 통계 |
| POST | `/api/proxy/restart` | config 파일 touch (프록시 재로드 트리거) |
## Hot reload 동작
- API 가 `data/config.json` 을 atomic rename (`tempfile + os.replace`) 으로 갱신
- 프록시가 2초 간격으로 mtime 폴링, 변경 감지 시 메모리 캐시 재로드
- `proxy.enabled` 가 바뀌면 리스너를 재시작 (켜기/끄기), 그 외(도메인/백엔드)는 다음 연결부터 즉시 적용
- `POST /api/proxy/restart` 는 별도 신호 파일(`data/restart.signal`)을 touch 해서 프록시가 listener 를 강제로 stop → start 한다 (config 변경이 없어도 동작)
> **리스닝 포트 변경:** `proxy.listen_port` 는 docker-compose 의 `ports` 매핑과 짝이라 대시보드에서 편집할 수 없습니다. 바꾸려면 `data/config.json` 과 `docker-compose.yml` 의 `25565:25565` 매핑을 함께 수정한 뒤 `docker compose up -d --force-recreate proxy` 하세요.
## 보안 권장
- **대시보드 포트(8080)는 외부 포트포워딩 금지.** Proxmox 내부망 / VPN / SSH 터널을 통해서만 접근하세요.
- API 에는 인증이 없으므로 외부에 노출해야 하는 경우 nginx 앞단에 basic auth 또는 OAuth proxy 를 추가하세요.
- 프록시 컨테이너는 핸드셰이크 단계에서만 패킷을 검사하고, 그 외에는 단순 TCP 중계라서 MC 프로토콜 변경에 영향을 받지 않습니다.
## 디렉터리 구조
```
.
├── proxy/ # asyncio TCP 프록시
│ ├── main.py
│ ├── handshake.py
│ ├── config.py
│ ├── requirements.txt
│ └── Dockerfile
├── api/ # FastAPI 백엔드
│ ├── main.py
│ ├── config_io.py
│ ├── routes/
│ │ ├── config.py
│ │ ├── domains.py
│ │ ├── logs.py
│ │ └── status.py
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/ # React + Vite 대시보드
│ ├── src/
│ │ ├── pages/
│ │ │ ├── Dashboard.jsx
│ │ │ ├── Domains.jsx
│ │ │ ├── Logs.jsx
│ │ │ └── Settings.jsx
│ │ ├── App.jsx
│ │ ├── api.js
│ │ ├── main.jsx
│ │ └── styles.css
│ ├── index.html
│ ├── vite.config.js
│ ├── package.json
│ └── Dockerfile
├── nginx/
│ └── nginx.conf
├── data/ # 런타임 데이터 (git 무시; config.json, logs.db)
└── docker-compose.yml
```
## 로컬 개발 (Docker 없이)
각 서비스는 독립적으로 실행 가능합니다.
```bash
# proxy
cd proxy
MC_CONFIG_PATH=$(pwd)/../data/config.json \
MC_LOG_DB=$(pwd)/../data/logs.db \
python main.py
# api (다른 터미널)
cd api
pip install -r requirements.txt
MC_CONFIG_PATH=$(pwd)/../data/config.json \
MC_LOG_DB=$(pwd)/../data/logs.db \
uvicorn main:app --reload --port 8000
# frontend (또 다른 터미널)
cd frontend
npm install
npm run dev # http://localhost:3000, /api 는 8000 으로 자동 프록시
```
## 환경 변수
| 변수 | 기본값 | 적용 서비스 |
|-------------------|--------------------|-------------------|
| `MC_CONFIG_PATH` | `/data/config.json`| proxy, api |
| `MC_LOG_DB` | `/data/logs.db` | proxy, api |
## 라이선스
내부 사용 (TkrMagid).