# πŸ€– n8n AI Agent: Cloudflare Tunnel μžλ™ν™” κ°€μ΄λ“œ n8nμ—μ„œ Cloudflare Tunnelκ³Ό Nginx Proxy Managerλ₯Ό μžλ™μœΌλ‘œ μ„€μ •ν•˜λŠ” AI μ—μ΄μ „νŠΈ μ›Œν¬ν”Œλ‘œμš°μž…λ‹ˆλ‹€. ## πŸ—οΈ μ•„ν‚€ν…μ²˜ ```mermaid graph TD A[HTTP Request] --> B[n8n Webhook] B --> C[AI Agent 검증] C --> D[Cloudflare API] C --> E[NPM API] D --> F[DNS CNAME μ„€μ •] E --> G[ν”„λ‘μ‹œ 호슀트 생성] F --> H[μ™„λ£Œ 응닡] G --> H ``` ## πŸ“‹ 사전 μš”κ΅¬μ‚¬ν•­ ### 1. n8n μ„€μΉ˜ 및 μ‹€ν–‰ ```bash # Docker Compose둜 μ‹€ν–‰ (ꢌμž₯) - 곡식 이미지 μ‚¬μš© docker run -it --rm --name n8n -p 5678:5678 docker.n8n.io/n8nio/n8n # λ˜λŠ” npm으둜 μ„€μΉ˜ npm install n8n -g n8n start ``` > **주의**: `n8nio/n8n` λŒ€μ‹  곡식 λ ˆμ§€μŠ€νŠΈλ¦¬ `docker.n8n.io/n8nio/n8n` μ‚¬μš© ꢌμž₯ ### 2. Credentials μ„€μ • n8nμ—μ„œ λ‹€μŒ Credentialsλ₯Ό 생성해야 ν•©λ‹ˆλ‹€: #### Cloudflare Credential (`cloudflare`) ```json { "email": "your-email@example.com", "api_key": "your-global-api-key" } ``` #### NPM Credential (`npm`) - 선택사항 ```json { "url": "http://localhost:81", "email": "admin@example.com", "password": "changeme" } ``` ## πŸš€ μ›Œν¬ν”Œλ‘œμš° μ„€μΉ˜ ### 1. 메인 AI Agent μ›Œν¬ν”Œλ‘œμš° μž„ν¬νŠΈ 1. n8n λŒ€μ‹œλ³΄λ“œμ—μ„œ **"Import from file"** 선택 2. `n8n-cf-tunnel-workflow.json` 파일 μ—…λ‘œλ“œ 3. Credentials μ—°κ²°: - **cloudflare**: Cloudflare credential 선택 - **npm**: NPM credential 선택 (μžˆλŠ” 경우) ### 2. Webhook API μ›Œν¬ν”Œλ‘œμš° μž„ν¬νŠΈ 1. `n8n-webhook-workflow.json` 파일 μž„ν¬νŠΈ 2. **Execute CF Agent** λ…Έλ“œμ—μ„œ: - `CF-TUNNEL-AI-AGENT-WORKFLOW-ID`λ₯Ό μ‹€μ œ μ›Œν¬ν”Œλ‘œμš° ID둜 λ³€κ²½ ## πŸ“‘ API μ‚¬μš©λ²• ### HTTP POST μš”μ²­ ```bash curl -X POST "http://localhost:5678/webhook/cf-tunnel" \ -H "Content-Type: application/json" \ -d '{ "domain": "example.com", "subdomain": "app", "service_ip": "192.168.1.100", "service_port": "8080" }' ``` ### 성곡 응닡 (200) ```json { "success": true, "domain": "app.example.com", "tunnel_id": "0adb287c-10e2-4f1d-af4c-8e083ed878d4", "tunnel_domain": "0adb287c-10e2-4f1d-af4c-8e083ed878d4.cfargotunnel.com", "service": "192.168.1.100:8080", "dns_configured": true, "npm_configured": true, "npm_host_id": "123", "timestamp": "2024-09-07T01:00:00.000Z", "message": "πŸš€ AI Agent μ™„λ£Œ: app.example.com β†’ 192.168.1.100:8080" } ``` ### μ—λŸ¬ 응닡 (400/500) ```json { "error": true, "message": "Missing required fields: domain, subdomain", "required_fields": ["domain", "subdomain", "service_ip", "service_port"], "example": { "domain": "example.com", "subdomain": "app", "service_ip": "192.168.1.100", "service_port": "8080" } } ``` ## πŸ”§ μ»€μŠ€ν„°λ§ˆμ΄μ§• ### 1. 터널 ID λ³€κ²½ 각 μ›Œν¬ν”Œλ‘œμš°μ˜ **Initialize Config** λ…Έλ“œμ—μ„œ: ```javascript tunnel_id: 'YOUR-TUNNEL-ID-HERE', tunnel_domain: 'YOUR-TUNNEL-ID-HERE.cfargotunnel.com', ``` ### 2. 계정 ID λ³€κ²½ ```javascript account_id: 'YOUR-ACCOUNT-ID-HERE' ``` ### 3. NPM μ„€μ • μˆ˜μ • **Create NPM Proxy** λ…Έλ“œμ—μ„œ SSL, 캐싱 λ“± μ„€μ • λ³€κ²½ κ°€λŠ₯: ```json { "ssl_forced": true, "caching_enabled": true, "certificate_id": 1, "meta": { "letsencrypt_agree": true, "dns_challenge": true } } ``` ## πŸ”’ λ³΄μ•ˆ 고렀사항 ### 1. API ν‚€ 보호 - Vault 연동 μ‚¬μš© ꢌμž₯ - n8n Credentials에 직접 μ €μž₯ μ‹œ μ•”ν˜Έν™” 확인 - μ΅œμ†Œ κΆŒν•œ 원칙 적용 ### 2. μ›Ήν›… λ³΄μ•ˆ ```javascript // Webhook λ…Έλ“œμ— 인증 μΆ”κ°€ const authHeader = $input.all()[0].headers.authorization; if (!authHeader || authHeader !== 'Bearer YOUR-SECRET-TOKEN') { return [{ json: { error: true, message: 'Unauthorized' } }]; } ``` ### 3. Rate Limiting n8nμ—μ„œ μ‹€ν–‰ μ œν•œ μ„€μ •: - **Execution Timeout**: 300초 - **Max Executions**: μ‹œκ°„λ‹Ή μ œν•œ μ„€μ • ## πŸ“Š λͺ¨λ‹ˆν„°λ§ 및 λ‘œκΉ… ### 1. μ‹€ν–‰ 둜그 확인 n8n λŒ€μ‹œλ³΄λ“œ β†’ **Executions** β†’ μ›Œν¬ν”Œλ‘œμš° μ‹€ν–‰ 기둝 확인 ### 2. μ—λŸ¬ μ•Œλ¦Ό μ„€μ • Error Workflow μƒμ„±ν•˜μ—¬ μ‹€νŒ¨ μ‹œ μ•Œλ¦Ό λ°œμ†‘: ```json { "name": "CF-Tunnel-Error-Handler", "nodes": [ { "name": "Send Slack Alert", "type": "n8n-nodes-base.slack" } ] } ``` ### 3. μ„±λŠ₯ λ©”νŠΈλ¦­ - 평균 μ‹€ν–‰ μ‹œκ°„: ~30-60초 - 성곡λ₯ : >95% λͺ©ν‘œ - API 응닡 μ‹œκ°„ λͺ¨λ‹ˆν„°λ§ ## πŸ§ͺ ν…ŒμŠ€νŠΈ μ‹œλ‚˜λ¦¬μ˜€ ### 1. κΈ°λ³Έ ν…ŒμŠ€νŠΈ ```bash # μƒˆ 도메인 생성 curl -X POST "http://localhost:5678/webhook/cf-tunnel" \ -H "Content-Type: application/json" \ -d '{ "domain": "test.com", "subdomain": "api", "service_ip": "127.0.0.1", "service_port": "3000" }' ``` ### 2. κΈ°μ‘΄ 도메인 μ—…λ°μ΄νŠΈ ```bash # 같은 subdomain.domain으둜 λ‹€λ₯Έ μ„œλΉ„μŠ€λ‘œ μ—…λ°μ΄νŠΈ curl -X POST "http://localhost:5678/webhook/cf-tunnel" \ -H "Content-Type: application/json" \ -d '{ "domain": "test.com", "subdomain": "api", "service_ip": "127.0.0.1", "service_port": "4000" }' ``` ### 3. μ—λŸ¬ ν…ŒμŠ€νŠΈ ```bash # 잘λͺ»λœ IP μ£Όμ†Œ curl -X POST "http://localhost:5678/webhook/cf-tunnel" \ -H "Content-Type: application/json" \ -d '{ "domain": "test.com", "subdomain": "api", "service_ip": "999.999.999.999", "service_port": "3000" }' ``` ## πŸš€ ν”„λ‘œλ•μ…˜ 배포 ### 1. Docker Compose μ„€μ • (λ³΄μ•ˆ κ°•ν™”) ```yaml version: '3.8' services: n8n: image: docker.n8n.io/n8nio/n8n ports: - "127.0.0.1:5678:5678" environment: # κΈ°λ³Έ μ„€μ • - N8N_HOST=0.0.0.0 - N8N_PORT=5678 - N8N_PROTOCOL=https - NODE_ENV=production # λ³΄μ•ˆ μ„€μ • (ν•„μˆ˜) - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} # openssl rand -hex 32 둜 생성 - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true # 인증 μ„€μ • - N8N_BASIC_AUTH_ACTIVE=true - N8N_BASIC_AUTH_USER=admin - N8N_BASIC_AUTH_PASSWORD=${N8N_ADMIN_PASSWORD} # λ³΄μ•ˆ κ°•ν™” μ˜΅μ…˜ - N8N_PUBLIC_API_DISABLED=true # Public API λΉ„ν™œμ„±ν™” - N8N_RUNNERS_ENABLED=true # Task Runner ν™œμ„±ν™” - N8N_PROXY_HOPS=1 # λ¦¬λ²„μŠ€ ν”„λ‘μ‹œ μ‚¬μš© μ‹œ # Webhook μ„€μ • - WEBHOOK_URL=https://n8n.yourdomain.com/ # νƒ€μž„μ‘΄ - GENERIC_TIMEZONE=Asia/Seoul - TZ=Asia/Seoul volumes: - n8n_data:/home/node/.n8n - ./local-files:/files restart: unless-stopped healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:5678/healthz"] interval: 30s timeout: 10s retries: 3 volumes: n8n_data: ``` ### ν™˜κ²½λ³€μˆ˜ 파일 (.env) ```bash # μ•”ν˜Έν™” ν‚€ 생성 (졜초 1회) openssl rand -hex 32 # .env 파일 μ˜ˆμ‹œ N8N_ENCRYPTION_KEY=your_generated_key_here N8N_ADMIN_PASSWORD=secure_password_here ``` > **μ€‘μš”**: `N8N_ENCRYPTION_KEY`λŠ” DB에 μ €μž₯λ˜λŠ” credential을 μ•”ν˜Έν™”ν•©λ‹ˆλ‹€. λΆ„μ‹€ μ‹œ λͺ¨λ“  credential μž¬μ„€μ • ν•„μš”. ### 2. λ¦¬λ²„μŠ€ ν”„λ‘μ‹œ μ„€μ • (λ³΄μ•ˆ 헀더 포함) ```nginx server { listen 80; server_name n8n.yourdomain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name n8n.yourdomain.com; ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem; # λ³΄μ•ˆ 헀더 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; location / { proxy_pass http://127.0.0.1:5678; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Connection ""; proxy_buffering off; proxy_read_timeout 300s; } } ``` ## πŸ“ μΆ”κ°€ κΈ°λŠ₯ 아이디어 1. **Bulk API**: μ—¬λŸ¬ 도메인 ν•œλ²ˆμ— μ„€μ • 2. **Status Check**: 터널 μƒνƒœ λͺ¨λ‹ˆν„°λ§ API 3. **SSL Certificate**: Let's Encrypt μžλ™ μ„€μ • 4. **Health Check**: μ„œλΉ„μŠ€ μƒνƒœ 확인 및 μ•Œλ¦Ό 5. **Rollback**: μ„€μ • 되돌리기 κΈ°λŠ₯ --- 이제 n8nμ—μ„œ Cloudflare 터널을 μžλ™ν™”ν•˜λŠ” AI μ—μ΄μ „νŠΈκ°€ μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€! πŸ€–βœ¨