☁️ Cloudflare Tunnel

거래 추적기 터널 배포 가이드

알파 서버 내부 앱을 deals.rinda.ai 로 안전하게 외부 공개하기 — 호스트 포트 0개, 오직 Cloudflare 터널로만.

대상 deal-tracker (rinda-html-share 패턴) · rinda.ai (grinda) · 작성 2026-06-30 · 접속 정상

한 일 5줄 요약

  1. 알파 서버에 deal-tracker-app 은 떠 있으나 짝이 되는 cloudflared 터널 컨테이너가 없고 .envTUNNEL_TOKEN 이 비어 있는 것을 레퍼런스(rinda-html-share)와 대조해 확인.
  2. grinda 계정에 원격관리형 터널 deal-tracker 생성(config_src=cloudflare) 후 연결 토큰 발급.
  3. 터널 ingress deals.rinda.ai → http://deal-tracker-app:3001 설정 + DNS CNAME(proxied)<tunnel-id>.cfargotunnel.com 으로 생성.
  4. 토큰을 알파 ~/deal-tracker/.env 에 주입하고 docker compose up -d cloudflared 로 기동 → 터널 healthy (4 conn).
  5. curl 은 403(Super Bot Fight Mode 가 자동화 트래픽 차단) 이지만 실제 브라우저로 deals.rinda.ai 정상 접속·렌더 확인(거래 235건). 배포 완료.
핵심 깨달음 “접속 안 됨”의 정체는 배포 오류가 아니라 Cloudflare Super Bot Fight Mode 였다. sbfm_definitely_automated: block 설정이 curl·스크립트 같은 자동화 요청을 엣지에서 403 처리한다(JA3/JA4 핑거프린트 기반이라 User-Agent 위장도 무효). 레퍼런스 html.rinda.ai 도 curl 로는 동일하게 403 → 브라우저로 검증하는 게 정답.

1 터널 배포 절차 (재현용)

app 컨테이너가 이미 내부망에 떠 있다는 전제. 호스트 포트는 절대 열지 않는다.

  1. 터널 생성 (원격관리형)
    # grinda 계정 ID 와 인증은 STEP 4(인증정보) 참고
    curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCT/cfd_tunnel" \
      -H "X-Auth-Email: $EMAIL" -H "X-Auth-Key: $KEY" \
      -H "Content-Type: application/json" \
      --data '{"name":"deal-tracker","config_src":"cloudflare"}'
    # → 응답의 result.id 가 TUNNEL_ID
  2. 연결 토큰 발급
    curl "https://api.cloudflare.com/client/v4/accounts/$ACCT/cfd_tunnel/$TID/token" \
      -H "X-Auth-Email: $EMAIL" -H "X-Auth-Key: $KEY"
    # → result(문자열)가 cloudflared 가 쓸 TUNNEL_TOKEN
  3. ingress 설정 — 어떤 호스트를 어느 내부 서비스로
    curl -X PUT ".../cfd_tunnel/$TID/configurations" \
      -H "X-Auth-Email: $EMAIL" -H "X-Auth-Key: $KEY" \
      --data '{"config":{"ingress":[
        {"hostname":"deals.rinda.ai","service":"http://deal-tracker-app:3001"},
        {"service":"http_status:404"}
      ]}}'
    왜 컨테이너명? cloudflared 가 같은 도커 네트워크(deal-tracker-net) 안에 있어 deal-tracker-app 이라는 컨테이너명으로 DNS 해석된다. 그래서 호스트 포트가 필요 없다.
  4. DNS CNAME 생성 (proxied)
    curl -X POST ".../zones/$ZONE/dns_records" \
      -H "X-Auth-Email: $EMAIL" -H "X-Auth-Key: $KEY" \
      --data '{"type":"CNAME","name":"deals",
        "content":"<TUNNEL_ID>.cfargotunnel.com","proxied":true}'
  5. 토큰 주입 + 기동
    # 알파 서버 ~/deal-tracker/.env
    TUNNEL_TOKEN=<발급받은 토큰>
    
    # 컨테이너 기동 (app 은 그대로, cloudflared 만 새로)
    cd ~/deal-tracker && docker compose up -d cloudflared
  6. 검증 — 브라우저로
    # 터널 상태: healthy / conns=4 확인
    docker logs deal-tracker-cloudflared | grep "Registered tunnel"
    
    # curl 은 403 나도 정상(Bot Fight). 실제 브라우저로 접속 확인.

2 Cloudflare 스킬 사용법

Claude Code 의 /cloudflare 스킬 — DNS·Workers·Pages·R2·터널 등 자연어로 관리.

항목내용
호출/cloudflare 또는 “클라우드플레어로 …” 자연어
기본 계정personal (wks0968@gmail.com). grinda 소유 존(rinda.ai·grinda.ai·eodisalji.com) 작업일 때만 grinda 로 전환
주요 그룹Zone · Workers · KV · R2 · D1 · Analytics · 환경변수 · 버전
DNS 수정MCP 미지원 → curl fallback (스킬 본문에 패턴 내장)
공개 호스팅단순 HTML 은 Pages(무료·무제한 대역폭) 추천 — 이 가이드도 그렇게 배포됨

Pages 배포 (wrangler) — 이 문서 배포에 쓴 방법

# personal 자격증명 로드
CREDS=$(cat ~/.claude/credentials/cloudflare.json)
export CLOUDFLARE_EMAIL=$(echo "$CREDS" | python3 -c "import sys,json;print(json.load(sys.stdin)['personal']['email'])")
export CLOUDFLARE_API_KEY=$(echo "$CREDS" | python3 -c "import sys,json;print(json.load(sys.stdin)['personal']['global_api_key'])")
export CLOUDFLARE_ACCOUNT_ID=$(echo "$CREDS" | python3 -c "import sys,json;print(json.load(sys.stdin)['personal']['account_id'])")

wrangler pages project create <name> --production-branch=main
wrangler pages deploy <dir> --project-name=<name> --branch=main --commit-dirty=true
# → https://<name>.pages.dev/

3 인증정보 발급 방법

스킬·스크립트가 읽는 자격증명 파일과, Cloudflare 쪽에서 키를 어떻게 얻는지.

① 자격증명 파일 구조

경로: ~/.claude/credentials/cloudflare.json — 계정별 블록으로 분리.

{
  "personal": {
    "email": "wks0968@gmail.com",
    "global_api_key": "<Global API Key>",
    "account_id": "<Account ID>"
  },
  "grinda": {
    "email": "admin@grinda.ai",
    "global_api_key": "<Global API Key>"
  }
}
보안 이 파일은 절대 깃·로그·외부로 노출 금지. Global API Key 는 계정 전권이라 유출 시 즉시 재발급(Roll) 해야 한다.

② Global API Key 발급 (대시보드)

단계위치
1dash.cloudflare.com → 우상단 프로필 → My Profile
2좌측 API Tokens
3하단 API Keys → Global API Key → View (계정 비밀번호 재입력)
4키 복사 → 위 JSON 의 global_api_key 에 기입. 이메일은 로그인 이메일.

③ Account ID 확인

대시보드에서 도메인 선택 → 우측 사이드바 API 섹션에 Account ID·Zone ID 표시. 또는 API 로:

curl "https://api.cloudflare.com/client/v4/zones/$ZONE" \
  -H "X-Auth-Email: $EMAIL" -H "X-Auth-Key: $KEY" \
  | python3 -c "import sys,json;print(json.load(sys.stdin)['result']['account']['id'])"

④ (권장) Scoped API Token

Global Key 대신 운영에서는 권한을 좁힌 API Token 사용을 권장. 같은 API Tokens 화면에서 Create Token → 필요한 권한만 부여(예: Zone:DNS:Edit, Account:Cloudflare Tunnel:Edit). 헤더는 X-Auth-Email/X-Auth-Key 대신 Authorization: Bearer <token>.

4 빠른 참조

리소스
앱 URLhttps://deals.rinda.ai/
rinda.ai Zone ID730c115be5ac17fb09629d6ec7c8b669
배포 디렉터리(알파)~/deal-tracker
컨테이너deal-tracker-app (3001) + deal-tracker-cloudflared
내부 네트워크deal-tracker-net (bridge)
레퍼런스 패턴rinda-html-sharehtml.rinda.ai