diff --git a/cloudflare-vault-integration.md b/cloudflare-vault-integration.md index 4506666..f193781 100644 --- a/cloudflare-vault-integration.md +++ b/cloudflare-vault-integration.md @@ -1,69 +1,540 @@ -# Cloudflare Vault 통합 가이드 +# 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 로그인 -### 1. **API Token 방식 (권장)** ```bash -# Vault에 저장 -vault kv put secret/cloudflare/tokens \ - api_token="your-api-token" \ +# 환경변수 설정 +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" - -# 사용 -export CLOUDFLARE_API_TOKEN=$(vault kv get -field=api_token secret/cloudflare/tokens) ``` -### 2. **Global API Key 방식 (현재)** -```bash -# 현재 Vault 구조 -secret/cloudflare/ - ├── email - └── api_key +### 3. Tunnel 크레덴셜 저장 -# 사용 -export CF_API_EMAIL=$(vault kv get -field=email secret/cloudflare) -export CF_API_KEY=$(vault kv get -field=api_key secret/cloudflare) -``` - -### 3. **Tunnel 별 크레덴셜** ```bash -# Vault에 터널별로 저장 +# cloudflared tunnel 생성 시 credentials.json 내용 저장 vault kv put secret/cloudflare/tunnels/production \ - tunnel_id="uuid" \ - tunnel_secret="base64-secret" \ - account_id="account-id" + 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 '.data.data' > ~/.cloudflared/production.json + 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 -gx CF_API_EMAIL (vault kv get -field=email secret/cloudflare) - set -gx CF_API_KEY (vault kv get -field=api_key secret/cloudflare) - echo "Cloudflare 인증 설정 완료" + 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 함수 + ```bash -alias cf-auth='eval $(vault kv get -format=json secret/cloudflare | jq -r ".data.data | to_entries[] | \"export CF_\" + (.key | ascii_upcase) + \"=\" + .value")' +# ~/.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 ``` -## 보안 고려사항 +--- -1. **최소 권한 원칙**: API Token 사용 시 필요한 권한만 부여 -2. **임시 인증**: 환경변수는 세션 종료 시 자동 삭제 -3. **파일 권한**: `chmod 600 ~/.cloudflared/*` -4. **Vault 통합**: 모든 크레덴셜은 Vault에서 중앙 관리 +## Terraform 통합 -## 추천 워크플로우 +### Provider 설정 -1. Vault에 API Token 저장 (Global Key 대신) -2. 필요시 환경변수로 로드 -3. 작업 완료 후 환경변수 정리 -4. CI/CD에서는 Vault Agent 사용 \ No newline at end of file +```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*