# Cloudflare + HashiCorp Vault 통합 가이드 > Cloudflare API 크레덴셜을 Vault에서 안전하게 관리하는 완벽 가이드 ## 목차 1. [개요](#개요) 2. [아키텍처](#아키텍처) 3. [Vault 초기 설정](#vault-초기-설정) 4. [Cloudflare API Token 생성](#cloudflare-api-token-생성) 5. [Vault에 크레덴셜 저장](#vault에-크레덴셜-저장) 6. [CLI 사용법](#cli-사용법) 7. [자동화 스크립트](#자동화-스크립트) 8. [Terraform 통합](#terraform-통합) 9. [CI/CD 통합](#cicd-통합) 10. [문제 해결](#문제-해결) --- ## 개요 ### 왜 Vault를 사용하는가? | 문제 | Vault 해결책 | |------|-------------| | 크레덴셜 하드코딩 | 중앙 집중식 시크릿 관리 | | 평문 저장 | 암호화된 저장소 | | 무제한 유효기간 | 동적 시크릿, TTL 설정 | | 감사 불가 | 모든 접근 로깅 | | 권한 관리 어려움 | 세분화된 ACL 정책 | ### 지원하는 Cloudflare 크레덴셜 | 유형 | 용도 | 권장 | |------|------|------| | **API Token** | 특정 권한만 부여 | ✅ 권장 | | **Global API Key** | 전체 권한 | ⚠️ 레거시 | | **Tunnel Credentials** | cloudflared 터널 | ✅ 필수 | | **Origin CA Key** | 오리진 인증서 | 필요시 | --- ## 아키텍처 ``` ┌─────────────────────────────────────────────────────────────┐ │ HashiCorp Vault │ │ (https://vault.anvil.it.com) │ ├─────────────────────────────────────────────────────────────┤ │ secret/cloudflare/ │ │ ├── tokens/ │ │ │ ├── dns-edit # DNS 편집용 토큰 │ │ │ ├── zone-read # Zone 읽기 전용 │ │ │ └── workers-deploy # Workers 배포용 │ │ ├── tunnels/ │ │ │ ├── production # 프로덕션 터널 │ │ │ └── staging # 스테이징 터널 │ │ └── global/ # Global API Key (레거시) │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ Terraform │ │ n8n │ │ CI/CD │ │ │ │ Workflows │ │ Pipeline │ └───────────┘ └───────────┘ └───────────┘ ``` --- ## Vault 초기 설정 ### 1. Vault 로그인 ```bash # 환경변수 설정 export VAULT_ADDR="https://vault.anvil.it.com" # 토큰 로그인 vault login # 또는 토큰 직접 지정 vault login hvs.xxxxxxxxxxxxx ``` ### 2. KV Secrets Engine 활성화 ```bash # KV v2 시크릿 엔진 활성화 (이미 있으면 스킵) vault secrets enable -path=secret kv-v2 # 확인 vault secrets list ``` ### 3. Cloudflare 정책 생성 ```bash # 정책 파일 생성 cat > cloudflare-policy.hcl << 'EOF' # Cloudflare 토큰 읽기 권한 path "secret/data/cloudflare/*" { capabilities = ["read", "list"] } # 토큰 메타데이터 읽기 path "secret/metadata/cloudflare/*" { capabilities = ["read", "list"] } EOF # 정책 적용 vault policy write cloudflare-read cloudflare-policy.hcl # 정책 확인 vault policy read cloudflare-read ``` ### 4. AppRole 설정 (자동화용) ```bash # AppRole 활성화 vault auth enable approle # Role 생성 vault write auth/approle/role/cloudflare-automation \ token_policies="cloudflare-read" \ token_ttl=1h \ token_max_ttl=4h \ secret_id_ttl=24h # Role ID 확인 vault read auth/approle/role/cloudflare-automation/role-id # Secret ID 생성 vault write -f auth/approle/role/cloudflare-automation/secret-id ``` --- ## Cloudflare API Token 생성 ### Cloudflare 대시보드에서 생성 1. https://dash.cloudflare.com/profile/api-tokens 접속 2. **Create Token** 클릭 3. 템플릿 선택 또는 **Custom token** 생성 ### 권장 토큰 템플릿 #### DNS 편집용 토큰 | 권한 | 리소스 | |------|--------| | Zone - DNS - Edit | 특정 Zone 또는 All zones | | Zone - Zone - Read | 특정 Zone 또는 All zones | #### Workers 배포용 토큰 | 권한 | 리소스 | |------|--------| | Account - Workers Scripts - Edit | 계정 | | Account - Workers KV Storage - Edit | 계정 | | Account - Workers Routes - Edit | 계정 | #### Zone 읽기 전용 토큰 | 권한 | 리소스 | |------|--------| | Zone - Zone - Read | All zones | | Zone - DNS - Read | All zones | | Zone - Analytics - Read | All zones | ### API 토큰 테스트 ```bash # 토큰 유효성 검증 curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer YOUR_API_TOKEN" # 성공 응답 { "result": { "status": "active" }, "success": true } ``` --- ## Vault에 크레덴셜 저장 ### 1. API Token 저장 (권장) ```bash # DNS 편집용 토큰 저장 vault kv put secret/cloudflare/tokens/dns-edit \ api_token="your-dns-edit-token-here" \ account_id="your-account-id" \ description="DNS record management" # Workers 배포용 토큰 저장 vault kv put secret/cloudflare/tokens/workers-deploy \ api_token="your-workers-token-here" \ account_id="your-account-id" \ description="Workers deployment" # 확인 vault kv get secret/cloudflare/tokens/dns-edit ``` ### 2. Global API Key 저장 (레거시) ```bash # Global API Key 저장 (권장하지 않음) vault kv put secret/cloudflare/global \ email="your-email@example.com" \ api_key="your-global-api-key" \ account_id="your-account-id" ``` ### 3. Tunnel 크레덴셜 저장 ```bash # cloudflared tunnel 생성 시 credentials.json 내용 저장 vault kv put secret/cloudflare/tunnels/production \ tunnel_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \ account_tag="your-account-tag" \ tunnel_secret="base64-encoded-secret" # 또는 JSON 파일 전체 저장 vault kv put secret/cloudflare/tunnels/production \ credentials="$(cat ~/.cloudflared/xxxxxxxx.json | base64)" ``` ### 4. 시크릿 버전 관리 ```bash # 시크릿 버전 목록 vault kv metadata get secret/cloudflare/tokens/dns-edit # 특정 버전 읽기 vault kv get -version=2 secret/cloudflare/tokens/dns-edit # 시크릿 삭제 (소프트 삭제) vault kv delete secret/cloudflare/tokens/old-token # 영구 삭제 vault kv destroy -versions=1 secret/cloudflare/tokens/old-token ``` --- ## CLI 사용법 ### 환경변수로 로드 ```bash # API Token 방식 (권장) export CLOUDFLARE_API_TOKEN=$(vault kv get -field=api_token secret/cloudflare/tokens/dns-edit) # Global API Key 방식 (레거시) export CF_API_EMAIL=$(vault kv get -field=email secret/cloudflare/global) export CF_API_KEY=$(vault kv get -field=api_key secret/cloudflare/global) # Account ID export CLOUDFLARE_ACCOUNT_ID=$(vault kv get -field=account_id secret/cloudflare/tokens/dns-edit) ``` ### Tunnel credentials.json 복원 ```bash # JSON 파일로 추출 vault kv get -format=json secret/cloudflare/tunnels/production | \ jq -r '.data.data | { AccountTag: .account_tag, TunnelID: .tunnel_id, TunnelSecret: .tunnel_secret }' > ~/.cloudflared/production.json chmod 600 ~/.cloudflared/production.json ``` ### 한 번에 모든 크레덴셜 로드 ```bash # JSON에서 환경변수로 변환 eval $(vault kv get -format=json secret/cloudflare/tokens/dns-edit | \ jq -r '.data.data | to_entries[] | "export CF_\(.key | ascii_upcase)=\(.value)"') ``` --- ## 자동화 스크립트 ### Fish Shell 함수 ```fish # ~/.config/fish/functions/cf-auth.fish function cf-auth set -l token_name $argv[1] if test -z "$token_name" set token_name "dns-edit" end set -gx CLOUDFLARE_API_TOKEN (vault kv get -field=api_token secret/cloudflare/tokens/$token_name) set -gx CLOUDFLARE_ACCOUNT_ID (vault kv get -field=account_id secret/cloudflare/tokens/$token_name) echo "✅ Cloudflare 인증 설정 완료 (토큰: $token_name)" end function cf-auth-clear set -e CLOUDFLARE_API_TOKEN set -e CLOUDFLARE_ACCOUNT_ID set -e CF_API_EMAIL set -e CF_API_KEY echo "🧹 Cloudflare 환경변수 정리 완료" end ``` ### Bash 함수 ```bash # ~/.bashrc 또는 ~/.bash_aliases cf-auth() { local token_name="${1:-dns-edit}" export CLOUDFLARE_API_TOKEN=$(vault kv get -field=api_token "secret/cloudflare/tokens/${token_name}") export CLOUDFLARE_ACCOUNT_ID=$(vault kv get -field=account_id "secret/cloudflare/tokens/${token_name}") echo "✅ Cloudflare 인증 설정 완료 (토큰: ${token_name})" } cf-auth-clear() { unset CLOUDFLARE_API_TOKEN CLOUDFLARE_ACCOUNT_ID CF_API_EMAIL CF_API_KEY echo "🧹 Cloudflare 환경변수 정리 완료" } # 자동 완성 _cf_auth_completions() { local tokens=$(vault kv list -format=json secret/cloudflare/tokens 2>/dev/null | jq -r '.[]') COMPREPLY=($(compgen -W "${tokens}" -- "${COMP_WORDS[COMP_CWORD]}")) } complete -F _cf_auth_completions cf-auth ``` --- ## Terraform 통합 ### Provider 설정 ```hcl # providers.tf terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" version = "~> 4.0" } vault = { source = "hashicorp/vault" version = "~> 3.0" } } } provider "vault" { address = "https://vault.anvil.it.com" # 토큰은 VAULT_TOKEN 환경변수에서 자동 로드 } # Vault에서 Cloudflare 토큰 읽기 data "vault_kv_secret_v2" "cloudflare" { mount = "secret" name = "cloudflare/tokens/dns-edit" } provider "cloudflare" { api_token = data.vault_kv_secret_v2.cloudflare.data["api_token"] } ``` ### DNS 레코드 관리 예시 ```hcl # dns.tf data "cloudflare_zone" "main" { name = "example.com" } resource "cloudflare_record" "api" { zone_id = data.cloudflare_zone.main.id name = "api" value = "192.168.1.100" type = "A" proxied = true } ``` ### Tunnel 설정 예시 ```hcl # tunnel.tf data "vault_kv_secret_v2" "tunnel" { mount = "secret" name = "cloudflare/tunnels/production" } resource "cloudflare_tunnel" "production" { account_id = data.vault_kv_secret_v2.cloudflare.data["account_id"] name = "production-tunnel" secret = data.vault_kv_secret_v2.tunnel.data["tunnel_secret"] } ``` --- ## CI/CD 통합 ### GitHub Actions ```yaml # .github/workflows/deploy.yml name: Deploy to Cloudflare on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Import Secrets from Vault uses: hashicorp/vault-action@v2 with: url: https://vault.anvil.it.com method: approle roleId: ${{ secrets.VAULT_ROLE_ID }} secretId: ${{ secrets.VAULT_SECRET_ID }} secrets: | secret/data/cloudflare/tokens/workers-deploy api_token | CLOUDFLARE_API_TOKEN ; secret/data/cloudflare/tokens/workers-deploy account_id | CLOUDFLARE_ACCOUNT_ID - name: Deploy Worker uses: cloudflare/wrangler-action@v3 with: apiToken: ${{ env.CLOUDFLARE_API_TOKEN }} accountId: ${{ env.CLOUDFLARE_ACCOUNT_ID }} ``` ### GitLab CI ```yaml # .gitlab-ci.yml variables: VAULT_ADDR: "https://vault.anvil.it.com" .vault-auth: &vault-auth before_script: - export VAULT_TOKEN=$(vault write -field=token auth/approle/login role_id=$VAULT_ROLE_ID secret_id=$VAULT_SECRET_ID) - export CLOUDFLARE_API_TOKEN=$(vault kv get -field=api_token secret/cloudflare/tokens/dns-edit) deploy: <<: *vault-auth script: - wrangler deploy ``` --- ## 문제 해결 ### 일반적인 오류 | 오류 | 원인 | 해결 | |------|------|------| | `permission denied` | Vault 정책 부족 | 정책에 경로 추가 | | `token expired` | Vault 토큰 만료 | `vault login` 재실행 | | `secret not found` | 잘못된 경로 | `vault kv list` 로 확인 | | `authentication error` | CF 토큰 만료/삭제 | Cloudflare에서 토큰 재생성 | ### Vault 연결 확인 ```bash # Vault 상태 확인 vault status # 토큰 정보 확인 vault token lookup # 시크릿 경로 목록 vault kv list secret/cloudflare/ vault kv list secret/cloudflare/tokens/ ``` ### Cloudflare 토큰 검증 ```bash # 토큰 유효성 확인 curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer $(vault kv get -field=api_token secret/cloudflare/tokens/dns-edit)" | jq # 권한 확인 curl -s -X GET "https://api.cloudflare.com/client/v4/user" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" | jq '.result.id' ``` ### 디버그 모드 ```bash # Vault 상세 로그 VAULT_LOG_LEVEL=debug vault kv get secret/cloudflare/tokens/dns-edit # curl 상세 출력 curl -v -X GET "https://api.cloudflare.com/client/v4/zones" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" ``` --- ## 보안 체크리스트 - [ ] Global API Key 대신 API Token 사용 - [ ] 토큰별 최소 권한 원칙 적용 - [ ] Vault 정책으로 접근 제한 - [ ] 환경변수는 세션 종료 시 정리 - [ ] CI/CD에서 AppRole 사용 - [ ] 정기적인 토큰 로테이션 - [ ] Vault 감사 로그 활성화 --- ## 참고 자료 - [Cloudflare API Tokens](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) - [HashiCorp Vault KV Secrets](https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2) - [Vault AppRole Auth](https://developer.hashicorp.com/vault/docs/auth/approle) - [Terraform Cloudflare Provider](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs) --- *마지막 업데이트: 2026-01*