diff --git a/CLAUDE.md b/CLAUDE.md index 98b7465..269233d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,24 +33,32 @@ npx wrangler tail ``` src/ ├── index.ts # Main router, CORS, Queue consumer -├── config.ts # Configuration constants +├── config.ts # Configuration constants (LIMITS, TIMEOUTS, i18n) ├── types.ts # TypeScript type definitions ├── region-utils.ts # Region matching utilities ├── utils.ts # Re-exports from utils/ (backward compatibility) ├── utils/ # Modular utilities │ ├── index.ts # Central export point │ ├── http.ts # HTTP responses, CORS, escapeHtml -│ ├── validation.ts # Input validation, type guards +│ ├── validation.ts # Input validation, type guards, escapeLikePattern │ ├── bandwidth.ts # Bandwidth estimation & cost calculation -│ ├── cache.ts # Caching, rate limiting +│ ├── cache.ts # Caching, rate limiting (with time-based cleanup) │ ├── ai.ts # AI prompt sanitization │ └── exchange-rate.ts # Currency conversion +├── middleware/ # Request middleware +│ ├── index.ts # Central export point +│ ├── auth.ts # API key authentication +│ ├── origin.ts # Origin validation (restricts to .kappa-d8e.workers.dev) +│ ├── rate-limit.ts # Rate limiting middleware +│ ├── request-id.ts # Request ID generation +│ ├── security.ts # Security headers (CSP, HSTS, etc.) +│ └── user-id.ts # User ID extraction ├── repositories/ │ ├── AnvilServerRepository.ts # DB queries for Anvil servers │ └── ProvisioningRepository.ts # Users, deposits, orders (telegram-conversations) ├── services/ │ ├── ai-service.ts # AI recommendations & fallback logic -│ ├── provisioning-service.ts # Server provisioning workflow +│ ├── provisioning-service.ts # Server provisioning workflow + SSH key injection │ ├── vps-provider.ts # VPS provider abstract base class │ ├── linode-provider.ts # Linode API implementation │ └── vultr-provider.ts # Vultr API implementation @@ -58,7 +66,7 @@ src/ │ ├── health.ts # GET /api/health │ ├── servers.ts # GET /api/servers │ ├── recommend.ts # POST /api/recommend -│ ├── report.ts # GET /api/recommend/report +│ ├── report.ts # GET /api/recommend/report (CSP: style-src 'unsafe-inline') │ ├── provision.ts # POST/GET/DELETE /api/provision/* │ └── queue.ts # Queue consumer for async provisioning └── __tests__/ @@ -253,12 +261,16 @@ window.open(reportUrl); // Opens printable HTML - **XSS prevention**: `escapeHtml()` applied to all user data in HTML reports - **Input validation**: Comprehensive checks with length limits (tech_stack ≤20, use_case ≤500, region_preference ≤10 items) - **Cache integrity**: Validates cached JSON structure before returning -- **Rate limiting**: 60 req/min per IP with in-memory fallback when KV unavailable -- **SQL injection prevention**: All queries use parameterized statements via Repository pattern +- **Rate limiting**: 60 req/min per IP with in-memory fallback (time-based sorting for cleanup) +- **SQL injection prevention**: All queries use parameterized statements + `escapeLikePattern()` for LIKE queries - **Security headers**: CSP, HSTS, X-Frame-Options, X-Content-Type-Options +- **Origin validation**: Restricts browser requests to `.kappa-d8e.workers.dev` domain only - **API key protection**: Keys never logged, sanitized from error messages - **Prompt injection protection**: `sanitizeForAIPrompt()` filters malicious patterns +- **Password generation**: Rejection sampling for unbiased random character selection - **Type safety**: No `any` types - uses `unknown` + type guards +- **Request size limits**: Base64 data parameter max 100KB for report endpoint +- **Timeout protection**: AbortController with configurable timeouts for all external APIs ### AI Fallback System (`services/ai-service.ts`) @@ -324,17 +336,67 @@ On failure: Refund balance + status: failed - Linode (`linode-provider.ts`) - Vultr (`vultr-provider.ts`) +### Admin SSH Key for Server Recovery + +Admin SSH key is automatically added to all provisioned servers for emergency recovery access. + +**Environment Variables** (set via `wrangler secret put`): +``` +ADMIN_SSH_PUBLIC_KEY # Linode: public key string (ssh-rsa AAAA... or ssh-ed25519 AAAA...) +ADMIN_SSH_KEY_ID_VULTR # Vultr: pre-registered SSH key ID (from Vultr dashboard/API) +``` + +**Provider Differences**: +| Provider | Parameter | Value Type | How to Set | +|----------|-----------|------------|------------| +| Linode | `authorized_keys` | Public key string | Direct: `ssh-ed25519 AAAA...` | +| Vultr | `sshkey_id` | Key ID | Register via dashboard/API first | + +**Vultr SSH Key Registration** (one-time setup): +```bash +# Register key via Vultr API +curl -X POST "https://api.vultr.com/v2/ssh-keys" \ + -H "Authorization: Bearer $VULTR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"name":"anvil-admin","ssh_key":"ssh-ed25519 AAAA..."}' + +# Response: {"ssh_key":{"id":"cb676a46-66fd-4dfb-..."}} +# Use this ID for ADMIN_SSH_KEY_ID_VULTR +``` + +**Password Recovery Options**: +| Provider | API Reset | Console Access | +|----------|-----------|----------------| +| Linode | ✅ `POST /linode/instances/{id}/password` | ✅ Lish via dashboard | +| Vultr | ❌ Not available | ✅ View Console in dashboard | + ### Configuration (`config.ts`) Centralized limits and constants: ```typescript LIMITS = { MAX_REQUEST_BODY_BYTES: 10240, // 10KB - CACHE_TTL_SECONDS: 300, // 5 minutes - RATE_LIMIT_MAX_REQUESTS: 60, // per minute - MAX_AI_CANDIDATES: 15, // reduce API cost + CACHE_TTL_SECONDS: 300, // 5 minutes + RATE_LIMIT_MAX_REQUESTS: 60, // per minute + MAX_AI_CANDIDATES: 15, // reduce API cost MAX_TECH_STACK: 20, MAX_USE_CASE_LENGTH: 500, + MAX_REGION_PREFERENCE: 10, + MAX_REGION_LENGTH: 50, + MAX_BASE64_DATA_LENGTH: 100000, // ~75KB decoded +} + +TIMEOUTS = { + AI_REQUEST_MS: 30000, // 30 seconds for OpenAI + VPS_PROVIDER_API_MS: 60000, // 60 seconds for Linode/Vultr + TELEGRAM_API_MS: 10000, // 10 seconds for Telegram notifications +} + +TECH_CATEGORY_WEIGHTS = { + heavy_db: 0.3, // analytics, log server, reporting + medium_heavy_db: 0.5, // e-commerce, ERP, CRM + medium_db: 0.7, // API, SaaS, app backend + light_db: 1.0, // blog, portfolio, wiki } ``` @@ -366,7 +428,13 @@ max_retries = 3 dead_letter_queue = "provision-queue-dlq" # Secrets (via wrangler secret put) -# OPENAI_API_KEY, LINODE_API_KEY, VULTR_API_KEY, PROVISION_API_KEY +# OPENAI_API_KEY - OpenAI API key for GPT-4o-mini +# LINODE_API_KEY - Linode API token +# VULTR_API_KEY - Vultr API key +# PROVISION_API_KEY - API key for /api/provision/* endpoints +# BOT_TOKEN - Telegram bot token for notifications (optional) +# ADMIN_SSH_PUBLIC_KEY - Admin SSH public key for Linode (optional) +# ADMIN_SSH_KEY_ID_VULTR - Admin SSH key ID for Vultr (optional) ``` ## Testing @@ -429,7 +497,38 @@ curl -s -X DELETE "https://cloud-orchestrator.kappa-d8e.workers.dev/api/provisio ## Recent Changes -### CDN Cache Hit Rate (Latest) +### Security Hardening & Admin SSH Key (Latest) + +**Security Improvements**: +- CSP headers for HTML reports (`style-src 'unsafe-inline'`) +- Origin validation restricts to `.kappa-d8e.workers.dev` domain +- Base64 size limit (100KB) for report data parameter +- Rejection sampling for unbiased password generation +- SQL LIKE pattern escaping via `escapeLikePattern()` +- Rate limiter cleanup with time-based sorting + +**Performance Improvements**: +- Telegram API timeout (10s) with AbortController +- Centralized TIMEOUTS config for all external APIs +- VPS provider timeout configurable (default 60s) + +**New Features**: +- Admin SSH key support for server recovery + - `ADMIN_SSH_PUBLIC_KEY` for Linode (public key string) + - `ADMIN_SSH_KEY_ID_VULTR` for Vultr (pre-registered key ID) +- Middleware layer: auth, origin, rate-limit, request-id, security, user-id +- Idempotency key for order deduplication + +**Code Quality**: +- 404 status when no servers found (was 200 with empty array) +- Consolidated error logging to single JSON.stringify +- Import TECH_CATEGORY_WEIGHTS from config.ts + +**Files Added**: +- `src/middleware/{auth,origin,rate-limit,request-id,security,user-id}.ts` +- `migrations/004_add_idempotency_key.sql` + +### CDN Cache Hit Rate **New Feature**: CDN 캐시 히트율 기반 원본 서버 트래픽 및 비용 계산