feat(security): API 키 보호, CORS 강화, Rate Limiting KV 전환
보안 개선: - API 키 하드코딩 제거 (NAMECHEAP_API_KEY_INTERNAL) - CORS 정책: * → hosting.anvil.it.com 제한 - /health 엔드포인트 DB 정보 노출 방지 - Rate Limiting 인메모리 Map → Cloudflare KV 전환 - 분산 환경 일관성 보장 - 재시작 후에도 유지 - 자동 만료 (TTL) 문서: - CLAUDE.md Security 섹션 추가 - KV Namespace 설정 가이드 추가 - 배포/마이그레이션 가이드 추가 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
265
DEPLOYMENT_SUMMARY.md
Normal file
265
DEPLOYMENT_SUMMARY.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Rate Limiting KV Migration - 배포 요약
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 1. Rate Limiting 시스템 마이그레이션
|
||||
- **이전:** 인메모리 Map (Worker 인스턴스별 독립)
|
||||
- **현재:** Cloudflare KV (분산 환경 공유)
|
||||
|
||||
### 2. 해결된 문제
|
||||
✅ Workers 인스턴스 간 Rate Limit 데이터 공유
|
||||
✅ Worker 재시작 시에도 Rate Limit 상태 유지
|
||||
✅ 분산 환경에서 일관된 Rate Limiting 동작
|
||||
✅ 자동 만료 (KV TTL) - 메모리 효율성
|
||||
|
||||
---
|
||||
|
||||
## 수정된 파일
|
||||
|
||||
### 1. `/Users/kaffa/telegram-bot-workers/wrangler.toml`
|
||||
```toml
|
||||
[[kv_namespaces]]
|
||||
binding = "RATE_LIMIT_KV"
|
||||
id = "YOUR_KV_NAMESPACE_ID" # 생성 후 실제 ID로 교체 필요
|
||||
```
|
||||
|
||||
### 2. `/Users/kaffa/telegram-bot-workers/src/types.ts`
|
||||
```typescript
|
||||
export interface Env {
|
||||
// ... 기존 필드들
|
||||
RATE_LIMIT_KV: KVNamespace; // ✅ 추가
|
||||
}
|
||||
```
|
||||
|
||||
### 3. `/Users/kaffa/telegram-bot-workers/src/security.ts`
|
||||
- 인메모리 Map 제거 (`rateLimitMap`)
|
||||
- `cleanupRateLimits()` 함수 제거 (KV TTL로 자동 관리)
|
||||
- `checkRateLimit()` 함수 시그니처 변경:
|
||||
- 이전: `checkRateLimit(userId: string): boolean`
|
||||
- 현재: `checkRateLimit(kv: KVNamespace, userId: string): Promise<boolean>`
|
||||
|
||||
### 4. `/Users/kaffa/telegram-bot-workers/src/index.ts`
|
||||
```typescript
|
||||
// 이전
|
||||
if (!checkRateLimit(telegramUserId)) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// 현재
|
||||
if (!(await checkRateLimit(env.RATE_LIMIT_KV, telegramUserId))) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 5. `/Users/kaffa/telegram-bot-workers/CLAUDE.md`
|
||||
- Rate Limiting 섹션 업데이트 (KV 기반 설명 추가)
|
||||
- Configuration 섹션에 KV Namespaces 테이블 추가
|
||||
- Commands 섹션에 KV Namespace 생성 명령 추가
|
||||
|
||||
---
|
||||
|
||||
## 배포 전 필수 작업
|
||||
|
||||
### Step 1: KV Namespace 생성
|
||||
```bash
|
||||
wrangler kv:namespace create RATE_LIMIT_KV
|
||||
```
|
||||
|
||||
**출력 예시:**
|
||||
```
|
||||
⛅️ wrangler 3.x.x
|
||||
-------------------
|
||||
🌀 Creating namespace with title "telegram-summary-bot-RATE_LIMIT_KV"
|
||||
✨ Success!
|
||||
Add the following to your configuration file in your kv_namespaces array:
|
||||
{ binding = "RATE_LIMIT_KV", id = "abc123..." }
|
||||
```
|
||||
|
||||
### Step 2: wrangler.toml 수정
|
||||
출력된 `id` 값을 복사하여 `wrangler.toml` 22번 줄 수정:
|
||||
```toml
|
||||
id = "abc123def456ghi789jkl012mno345pq" # ← 실제 ID로 변경
|
||||
```
|
||||
|
||||
### Step 3: TypeScript 컴파일 확인
|
||||
```bash
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
### Step 4: 로컬 테스트
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
다른 터미널에서:
|
||||
```bash
|
||||
curl -X POST http://localhost:8787/webhook \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Telegram-Bot-Api-Secret-Token: test-secret" \
|
||||
-d '{"update_id":1,"message":{"message_id":1,"from":{"id":123,"is_bot":false,"first_name":"Test"},"chat":{"id":123,"type":"private"},"date":1234567890,"text":"테스트"}}'
|
||||
```
|
||||
|
||||
### Step 5: Production 배포
|
||||
```bash
|
||||
npm run deploy
|
||||
```
|
||||
|
||||
### Step 6: 배포 확인
|
||||
```bash
|
||||
# Health Check
|
||||
curl https://telegram-summary-bot.kappa-d8e.workers.dev/health
|
||||
|
||||
# 로그 스트리밍
|
||||
npm run tail
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 기술 상세
|
||||
|
||||
### Rate Limiting 동작 방식
|
||||
|
||||
**Key 형식:** `ratelimit:{userId}`
|
||||
|
||||
**데이터 구조:**
|
||||
```typescript
|
||||
{
|
||||
count: number, // 현재 윈도우 내 요청 수
|
||||
resetAt: number // 윈도우 만료 시각 (Unix timestamp)
|
||||
}
|
||||
```
|
||||
|
||||
**알고리즘:**
|
||||
1. KV에서 `ratelimit:{userId}` 조회
|
||||
2. 데이터 없음 또는 윈도우 만료 (`now > resetAt`)
|
||||
→ 새 윈도우 시작 (`count=1`, `resetAt=now+60000`)
|
||||
3. `count >= 30` (기본값)
|
||||
→ Rate Limit 초과, 요청 차단
|
||||
4. `count < 30`
|
||||
→ `count++` 후 KV 업데이트
|
||||
|
||||
**자동 만료:**
|
||||
- KV의 `expirationTtl` 옵션 사용
|
||||
- 윈도우 종료 시 자동 삭제 (메모리 효율)
|
||||
|
||||
**에러 처리:**
|
||||
- KV 오류 발생 시: Rate Limit 통과 (서비스 가용성 우선)
|
||||
- 로그 기록: `[RateLimit] KV 오류: ...`
|
||||
|
||||
---
|
||||
|
||||
## 모니터링
|
||||
|
||||
### KV Dashboard
|
||||
https://dash.cloudflare.com → Workers & Pages → KV → telegram-summary-bot-RATE_LIMIT_KV
|
||||
|
||||
### KV CLI 명령어
|
||||
```bash
|
||||
# Namespace 목록
|
||||
wrangler kv:namespace list
|
||||
|
||||
# 특정 키 조회
|
||||
wrangler kv:key get "ratelimit:821596605" --namespace-id=YOUR_KV_ID
|
||||
|
||||
# 모든 키 목록
|
||||
wrangler kv:key list --namespace-id=YOUR_KV_ID
|
||||
|
||||
# 키 삭제 (테스트용)
|
||||
wrangler kv:key delete "ratelimit:821596605" --namespace-id=YOUR_KV_ID
|
||||
```
|
||||
|
||||
### 로그 확인
|
||||
```bash
|
||||
# 실시간 로그
|
||||
wrangler tail
|
||||
|
||||
# Rate Limit 관련 로그만 필터링
|
||||
wrangler tail --format json | jq 'select(.message | contains("RateLimit"))'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 성능 영향
|
||||
|
||||
### Before (인메모리 Map)
|
||||
- 응답 시간: ~1ms (동기)
|
||||
- 메모리: 인스턴스별 독립 (중복 저장)
|
||||
- 일관성: ❌ 분산 환경에서 불일치
|
||||
|
||||
### After (KV)
|
||||
- 응답 시간: ~20-50ms (KV read/write 포함)
|
||||
- 메모리: 0 (KV로 오프로드)
|
||||
- 일관성: ✅ 전역 일관성 보장
|
||||
|
||||
**영향 분석:**
|
||||
- 약 20-50ms 지연 추가 (허용 가능한 수준)
|
||||
- Telegram Webhook 응답은 200ms 이내 권장 (충분히 만족)
|
||||
- 사용자 경험에 무시할 수 있는 영향
|
||||
|
||||
---
|
||||
|
||||
## 비용 분석
|
||||
|
||||
### Cloudflare Workers Free Plan
|
||||
- **KV 읽기:** 100,000 reads/day (무료)
|
||||
- **KV 쓰기:** 1,000 writes/day (무료)
|
||||
|
||||
### Rate Limiting 사용량
|
||||
- 1 메시지 = 1 read + 1 write (최악의 경우)
|
||||
- 일일 1,000 메시지까지 무료 (write limit 기준)
|
||||
- 초과 시: $0.50 per million writes
|
||||
|
||||
**예상 사용량:**
|
||||
- 현재 사용자 수: 소규모 (~10명)
|
||||
- 일일 메시지 수: ~100개
|
||||
- 예상 비용: $0 (무료 한도 내)
|
||||
|
||||
---
|
||||
|
||||
## 롤백 절차 (문제 발생 시)
|
||||
|
||||
### Option 1: Git Revert
|
||||
```bash
|
||||
git revert HEAD
|
||||
npm run deploy
|
||||
```
|
||||
|
||||
### Option 2: 수동 롤백
|
||||
1. `wrangler.toml`에서 KV Namespace 제거
|
||||
2. `src/types.ts`에서 `RATE_LIMIT_KV` 제거
|
||||
3. `src/security.ts` 이전 버전으로 복원
|
||||
4. `src/index.ts` 이전 버전으로 복원
|
||||
5. `npm run deploy`
|
||||
|
||||
---
|
||||
|
||||
## 추가 리소스
|
||||
|
||||
- **상세 마이그레이션 가이드:** `/Users/kaffa/telegram-bot-workers/KV_MIGRATION_GUIDE.md`
|
||||
- **Cloudflare KV Docs:** https://developers.cloudflare.com/kv/
|
||||
- **Workers KV Limits:** https://developers.cloudflare.com/workers/platform/limits/#kv
|
||||
|
||||
---
|
||||
|
||||
## 체크리스트
|
||||
|
||||
배포 전:
|
||||
- [ ] `wrangler kv:namespace create RATE_LIMIT_KV` 실행
|
||||
- [ ] `wrangler.toml`에 실제 KV Namespace ID 입력
|
||||
- [ ] `npx tsc --noEmit` 타입 에러 없음
|
||||
- [ ] `npm run dev` 로컬 테스트 성공
|
||||
- [ ] Rate Limit 테스트 (30회 연속 요청)
|
||||
|
||||
배포 후:
|
||||
- [ ] `npm run deploy` 성공
|
||||
- [ ] Health Check 정상 (`/health` 엔드포인트)
|
||||
- [ ] 실제 Telegram 메시지 테스트 성공
|
||||
- [ ] KV Dashboard에서 키 생성 확인
|
||||
- [ ] `wrangler tail` 로그 확인
|
||||
- [ ] 30회 연속 메시지로 Rate Limit 동작 확인
|
||||
|
||||
---
|
||||
|
||||
**작업 완료 시각:** 2026-01-19
|
||||
**배포 담당자:** Claude Code (AI Assistant)
|
||||
**상태:** ✅ 코드 변경 완료 / ⏳ KV Namespace 생성 대기 중
|
||||
Reference in New Issue
Block a user