From b87947e3e3400c10b5a0aaaa0ab3bb1083605778 Mon Sep 17 00:00:00 2001 From: kappa Date: Tue, 9 Sep 2025 15:33:36 +0900 Subject: [PATCH] Complete infrastructure and integration updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Infrastructure improvements: - Update CloudFront distribution with ACM certificate support - Enable custom domain aliases when certificate is available - Add comprehensive WAF outputs for CrowdSec integration - Update variables with current configuration defaults New files: - Add CrowdSec WAF integration documentation - Add sync script for CrowdSec to WAF automation - Add MCP configuration for development tools Configuration updates: - Align Terraform configuration with deployed state - Enable ACM certificate and Route53 DNS by default - Maintain HTTP-only origin protocol for compatibility ๐Ÿค– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .mcp.json | 39 +++++ CROWDSEC-WAF-INTEGRATION.md | 309 ++++++++++++++++++++++++++++++++++++ main.tf | 11 +- outputs.tf | 25 ++- security.tf | 48 +++++- sync-crowdsec-to-waf.sh | 90 +++++++++++ variables.tf | 3 +- 7 files changed, 515 insertions(+), 10 deletions(-) create mode 100644 .mcp.json create mode 100644 CROWDSEC-WAF-INTEGRATION.md create mode 100755 sync-crowdsec-to-waf.sh diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..b4f0438 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,39 @@ +{ + "mcpServers": { + "context7": { + "command": "npx", + "args": ["-y", "@upstash/context7-mcp@latest"] + }, + "cloudflare": { + "command": "npx", + "args": ["mcp-remote", "https://docs.mcp.cloudflare.com/sse"] + }, + "aws-api-mcp-server": { + "command": "uvx", + "args": ["awslabs.aws-api-mcp-server@latest"], + "env": { + "AWS_REGION": "ap-northeast-2" + } + }, + "aws-knowledge-mcp-server": { + "command": "uvx", + "args": [ + "mcp-proxy", + "--transport", + "streamablehttp", + "https://knowledge-mcp.global.api.aws" + ] + }, + "aws-serverless-mcp-server": { + "command": "uvx", + "args": [ + "awslabs.aws-serverless-mcp-server@latest", + "--allow-write", + "--allow-sensitive-data-access" + ], + "env": { + "AWS_REGION": "ap-northeast-2" + } + } + } +} diff --git a/CROWDSEC-WAF-INTEGRATION.md b/CROWDSEC-WAF-INTEGRATION.md new file mode 100644 index 0000000..483c460 --- /dev/null +++ b/CROWDSEC-WAF-INTEGRATION.md @@ -0,0 +1,309 @@ +# CrowdSec - AWS WAF ์‹ค์‹œ๊ฐ„ ํ†ตํ•ฉ ๊ฐ€์ด๋“œ + +## ๐Ÿ“‹ ๊ฐœ์š” + +์ด ๋ฌธ์„œ๋Š” CrowdSec์™€ AWS WAF ๊ฐ„์˜ ์‹ค์‹œ๊ฐ„ IP ์ฐจ๋‹จ ํ†ตํ•ฉ ์‹œ์Šคํ…œ ๊ตฌํ˜„ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. CrowdSec๊ฐ€ ํƒ์ง€ํ•œ ์•…์„ฑ IP๋ฅผ AWS Lambda๋ฅผ ํ†ตํ•ด ์ž๋™์œผ๋กœ WAF IP Set์— ์ถ”๊ฐ€/์ œ๊ฑฐํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ๋ณด์•ˆ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ—๏ธ ์•„ํ‚คํ…์ฒ˜ + +``` +CrowdSec Container โ†’ Webhook Notification โ†’ API Gateway โ†’ Lambda Function โ†’ AWS WAF IP Set โ†’ CloudFront Distribution +``` + +### ์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ + +1. **CrowdSec Container** (Incus) + - ๋ณด์•ˆ ์ด๋ฒคํŠธ ๊ฐ์ง€ ๋ฐ ๋ถ„์„ + - Nginx Proxy Manager ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง + - ์›นํ›… ์•Œ๋ฆผ ์ „์†ก + +2. **AWS API Gateway** + - ์›นํ›… ์—”๋“œํฌ์ธํŠธ ์ œ๊ณต + - Lambda ํ•จ์ˆ˜ ํŠธ๋ฆฌ๊ฑฐ + +3. **AWS Lambda Function** + - CrowdSec Alert ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ + - WAF IP Set ์—…๋ฐ์ดํŠธ ์ˆ˜ํ–‰ + +4. **AWS WAF v2** + - IP Set ๊ธฐ๋ฐ˜ ์ฐจ๋‹จ ๊ทœ์น™ + - CloudFront ๋ฐฐํฌ ๋ณดํ˜ธ + +## ๐Ÿš€ ๊ตฌํ˜„ ๋‹จ๊ณ„ + +### 1. ์ธํ”„๋ผ ๋ฐฐํฌ + +```bash +# OpenTofu ์ดˆ๊ธฐํ™” +tofu init + +# ์ธํ”„๋ผ ๋ฐฐํฌ +tofu apply -auto-approve +``` + +**๋ฐฐํฌ๋˜๋Š” ๋ฆฌ์†Œ์Šค:** +- Lambda ํ•จ์ˆ˜ ๋ฐ IAM ์—ญํ•  +- API Gateway ์›นํ›… ์—”๋“œํฌ์ธํŠธ +- WAF v2 IP Set ๋ฐ Web ACL +- CloudWatch ๋กœ๊ทธ ๊ทธ๋ฃน + +### 2. CrowdSec ์ปจํ…Œ์ด๋„ˆ ์„ค์ • + +#### ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ ๋ฐ ์„ค์ • +```bash +# ์ƒˆ ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ +incus launch ubuntu:24.04 crowdsec + +# CrowdSec ์„ค์น˜ +incus exec crowdsec -- apt update +incus exec crowdsec -- curl -s https://install.crowdsec.net | sh + +# Nginx Proxy Manager ์ปฌ๋ ‰์…˜ ์„ค์น˜ +incus exec crowdsec -- cscli collections install crowdsecurity/nginx-proxy-manager +incus exec crowdsec -- systemctl reload crowdsec +``` + +#### ์›นํ›… ์•Œ๋ฆผ ์„ค์ • +```bash +# ์•Œ๋ฆผ ์„ค์ • ํŒŒ์ผ ์ƒ์„ฑ +incus exec crowdsec -- tee /etc/crowdsec/notifications/aws-waf.yaml << 'EOF' +type: http +name: aws-waf +log_level: info +url: https://8zdmpjfnhh.execute-api.us-east-1.amazonaws.com/dev/webhook +method: POST +headers: + Content-Type: application/json + User-Agent: CrowdSec/1.7.0 +timeout: 10s +format: | + {{ .|toJson }} +EOF + +# ํ”„๋กœํ•„ ์„ค์ • +incus exec crowdsec -- tee /etc/crowdsec/profiles.yaml << 'EOF' +name: aws_waf_profile +filters: + - Alert.Remediation == true && Alert.GetScenario() startsWith "crowdsecurity/" +notifications: + - aws-waf +on_success: break +EOF + +# CrowdSec ์žฌ์‹œ์ž‘ +incus exec crowdsec -- systemctl restart crowdsec +``` + +### 3. Lambda ํ•จ์ˆ˜ ์ฝ”๋“œ + +**ํŒŒ์ผ ์œ„์น˜:** `/Users/kaffa/Projects/was-cf/lambda-crowdsec-waf.py` + +์ฃผ์š” ๊ธฐ๋Šฅ: +- CrowdSec Alert JSON ๊ตฌ์กฐ ์ฒ˜๋ฆฌ +- IP ์ฃผ์†Œ ์ถ”์ถœ ๋ฐ ๊ฒ€์ฆ +- WAF IP Set ์—…๋ฐ์ดํŠธ (์ถ”๊ฐ€/์ œ๊ฑฐ) +- ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฐ ๋กœ๊น… + +ํ•ต์‹ฌ ์ฒ˜๋ฆฌ ๋กœ์ง: +```python +# CrowdSec Alert ๊ตฌ์กฐ ์ฒ˜๋ฆฌ (toJson ํ˜•์‹) +if isinstance(webhook_data, list): + # Alert ๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ + for alert in webhook_data: + if 'decisions' in alert and alert['decisions']: + for decision in alert['decisions']: + # Source IP ์ถ”์ถœ + source_ip = alert.get('source', {}).get('ip', '') + # Decision ์ •๋ณด ํฌํ•จ + decision['source_ip'] = source_ip + decision['alert_uuid'] = alert.get('uuid', '') + decisions.append(decision) +``` + +## ๐Ÿ“Š WAF ๊ทœ์น™ ์šฐ์„ ์ˆœ์œ„ + +AWS WAF Web ACL ๊ทœ์น™ ์ˆœ์„œ (๋‚ฎ์€ ๋ฒˆํ˜ธ๊ฐ€ ๋†’์€ ์šฐ์„ ์ˆœ์œ„): + +1. **Priority 1: BlockedIPsRule** โ† CrowdSec IP ์ฐจ๋‹จ (์ตœ๊ณ  ์šฐ์„ ์ˆœ์œ„) - `aws-cf-dev-blocked-ips` IP Set ์‚ฌ์šฉ +2. **Priority 2: RateLimitRule** โ† ์ผ๋ฐ˜ ๋ ˆ์ดํŠธ ์ œํ•œ (10,000 req/5min) - ์ „์ฒด IP ๋Œ€์ƒ +3. **Priority 3: AWSManagedRulesCommonRuleSet** โ† AWS ๊ด€๋ฆฌํ˜• ๊ณตํ†ต ๊ทœ์น™ +4. **Priority 4: AWSManagedRulesKnownBadInputsRuleSet** โ† AWS ๊ด€๋ฆฌํ˜• ์•…์„ฑ ์ž…๋ ฅ ๊ทœ์น™ + +**โš ๏ธ ์ฐธ๊ณ :** ์ด์ „์— ์žˆ๋˜ `suspicious_ips` IP Set๊ณผ `SuspiciousIPsRateLimit` ๊ทœ์น™์€ ํ˜„์žฌ Lambda ๊ธฐ๋ฐ˜ ์‹ค์‹œ๊ฐ„ ํ†ตํ•ฉ์œผ๋กœ ๋Œ€์ฒด๋˜์–ด ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +## ๐Ÿงช ํ…Œ์ŠคํŠธ ๋ฐ ๊ฒ€์ฆ + +### 1. ์›นํ›… ์—”๋“œํฌ์ธํŠธ ํ…Œ์ŠคํŠธ +```bash +curl -X POST https://8zdmpjfnhh.execute-api.us-east-1.amazonaws.com/dev/webhook \ + -H "Content-Type: application/json" \ + -H "User-Agent: CrowdSec/1.7.0" \ + -d '[{ + "uuid": "test-uuid-123", + "machine_id": "test-machine", + "created_at": "2025-09-09T02:50:00Z", + "scenario": "test/security-scan", + "source": {"ip": "192.168.1.100"}, + "decisions": [{ + "value": "192.168.1.100", + "type": "ban", + "action": "add", + "scope": "ip" + }] + }]' +``` + +**์˜ˆ์ƒ ์‘๋‹ต:** +```json +{ + "success": true, + "message": "WAF IP Set updated successfully", + "ips_added": ["192.168.1.100"], + "ips_removed": [], + "total_ips": 1 +} +``` + +### 2. WAF IP Set ํ™•์ธ +```bash +# IP Set ๋‚ด์šฉ ์กฐํšŒ +aws wafv2 get-ip-set \ + --scope CLOUDFRONT \ + --id c43ff364-f3e2-43c7-8462-8fae20599d8d \ + --name aws-cf-dev-blocked-ips \ + --region us-east-1 \ + --query 'IPSet.Addresses' +``` + +### 3. CrowdSec ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ +```bash +# CrowdSec ์•Œ๋ฆผ ํ…Œ์ŠคํŠธ +incus exec crowdsec -- cscli notifications test aws-waf +``` + +## ๐Ÿ“ˆ ์„ค์น˜๋œ ๋ณด์•ˆ ์ปฌ๋ ‰์…˜ + +### Nginx Proxy Manager ์ปฌ๋ ‰์…˜ +- **์ปฌ๋ ‰์…˜:** `crowdsecurity/nginx-proxy-manager` +- **ํŒŒ์„œ:** `crowdsecurity/nginx-proxy-manager-logs` +- **HTTP ๋กœ๊ทธ:** `crowdsecurity/http-logs` + +### ์ฃผ์š” ๋ณด์•ˆ ์‹œ๋‚˜๋ฆฌ์˜ค (40+ ๊ฐœ) +- **Web ๊ณต๊ฒฉ:** XSS, SQLi, Path Traversal ํ”„๋กœ๋น™ +- **CVE ์ทจ์•ฝ์ :** Log4j, Spring4Shell, Apache ๋“ฑ +- **๊ด€๋ฆฌ์ž ์ธํ„ฐํŽ˜์ด์Šค:** ๋ฌด์ฐจ๋ณ„ ๋Œ€์ž… ๊ณต๊ฒฉ ๊ฐ์ง€ +- **๋ฐฑ๋„์–ด ์‹œ๋„:** ์•…์„ฑ ์Šคํฌ๋ฆฝํŠธ ์—…๋กœ๋“œ ๊ฐ์ง€ +- **WordPress:** ์Šค์บ” ๋ฐ ๋ฌด์ฐจ๋ณ„ ๋Œ€์ž… ๊ณต๊ฒฉ +- **๋„คํŠธ์›Œํฌ:** ์˜คํ”ˆ ํ”„๋ก์‹œ, ํฌ๋กค๋ง ๊ฐ์ง€ + +## ๐Ÿ”ง ๋ฆฌ์†Œ์Šค ์ •๋ณด + +### AWS ๋ฆฌ์†Œ์Šค +```bash +# CloudFront Distribution ID +E1XR8P4ENGP8RU + +# WAF Web ACL ID +d21d84c1-edb9-40af-9cdd-27f42f09c499 + +# WAF IP Set ID +c43ff364-f3e2-43c7-8462-8fae20599d8d + +# Lambda Function +aws-cf-dev-crowdsec-waf-updater + +# Webhook URL +https://8zdmpjfnhh.execute-api.us-east-1.amazonaws.com/dev/webhook +``` + +### Terraform ์ถœ๋ ฅ๊ฐ’ +```bash +tofu output +``` + +## ๐Ÿ” ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊ทธ + +### Lambda ํ•จ์ˆ˜ ๋กœ๊ทธ +```bash +# CloudWatch ๋กœ๊ทธ ํ™•์ธ +aws logs tail /aws/lambda/aws-cf-dev-crowdsec-waf-updater --follow +``` + +### CrowdSec ๋กœ๊ทธ +```bash +# CrowdSec ์„œ๋น„์Šค ๋กœ๊ทธ +incus exec crowdsec -- journalctl -u crowdsec -f + +# ์ฐจ๋‹จ ๊ฒฐ์ • ๋ชฉ๋ก +incus exec crowdsec -- cscli decisions list +``` + +### WAF ๋ฉ”ํŠธ๋ฆญ +- CloudWatch์—์„œ WAF ์ฐจ๋‹จ ๋ฉ”ํŠธ๋ฆญ ํ™•์ธ +- `BlockedIPsRule` ๋ฉ”ํŠธ๋ฆญ ๋ชจ๋‹ˆํ„ฐ๋ง + +## ๐Ÿ› ๏ธ ๋ฌธ์ œ ํ•ด๊ฒฐ + +### ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ + +1. **์›นํ›… ์—ฐ๊ฒฐ ์‹คํŒจ** + - ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ํ™•์ธ + - API Gateway URL ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ + - CrowdSec ์•Œ๋ฆผ ์„ค์ • ์žฌ๊ฒ€ํ†  + +2. **Lambda ํ•จ์ˆ˜ ์˜ค๋ฅ˜** + - CloudWatch ๋กœ๊ทธ ํ™•์ธ + - IAM ๊ถŒํ•œ ๊ฒ€์ฆ + - WAF IP Set ์ ‘๊ทผ ๊ถŒํ•œ ํ™•์ธ + +3. **IP๊ฐ€ ์ฐจ๋‹จ๋˜์ง€ ์•Š์Œ** + - WAF ๊ทœ์น™ ์šฐ์„ ์ˆœ์œ„ ํ™•์ธ + - CloudFront ๋ฐฐํฌ์™€ WAF ์—ฐ๊ฒฐ ์ƒํƒœ + - IP Set ์—…๋ฐ์ดํŠธ ์ƒํƒœ ํ™•์ธ + +### ๋””๋ฒ„๊น… ๋ช…๋ น์–ด +```bash +# CrowdSec ์ƒํƒœ ํ™•์ธ +incus exec crowdsec -- cscli metrics + +# ์•Œ๋ฆผ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ƒํƒœ +incus exec crowdsec -- cscli notifications list + +# Lambda ํ•จ์ˆ˜ ํ…Œ์ŠคํŠธ +aws lambda invoke --function-name aws-cf-dev-crowdsec-waf-updater response.json + +# WAF ๊ทœ์น™ ํ™•์ธ +aws wafv2 get-web-acl --scope CLOUDFRONT --id d21d84c1-edb9-40af-9cdd-27f42f09c499 --name aws-cf-dev-waf --region us-east-1 +``` + +## ๐Ÿš€ ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ + +1. **๋‹ค์ค‘ ํ™˜๊ฒฝ ์ง€์›** + - ๊ฐœ๋ฐœ/์Šคํ…Œ์ด์ง•/ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ๋ณ„ WAF ์—ฐ๊ฒฐ + - ํ™˜๊ฒฝ๋ณ„ IP Set ๋ถ„๋ฆฌ + +2. **๊ณ ๊ธ‰ ์•Œ๋ฆผ** + - Slack/Discord ํ†ตํ•ฉ + - ์ด๋ฉ”์ผ ์•Œ๋ฆผ ์ถ”๊ฐ€ + - ๋Œ€์‹œ๋ณด๋“œ ์—ฐ๋™ + +3. **IP ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ** + - ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” IP ์ž๋™ ์ œ์™ธ + - ์ง€์—ญ๋ณ„ IP ํ•„ํ„ฐ๋ง + +4. **์ž๋™ ํ•ด์ œ** + - ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์ž๋™ ์ฐจ๋‹จ ํ•ด์ œ + - ์œ„ํ—˜๋„ ๊ธฐ๋ฐ˜ ์ฐจ๋‹จ ๊ธฐ๊ฐ„ ์กฐ์ • + +## ๐Ÿ“ ์ฐธ๊ณ  ์ž๋ฃŒ + +- [CrowdSec Documentation](https://docs.crowdsec.net/) +- [AWS WAF Developer Guide](https://docs.aws.amazon.com/waf/) +- [Lambda Developer Guide](https://docs.aws.amazon.com/lambda/) +- [CrowdSec Hub](https://hub.crowdsec.net/) + +--- + +**๊ตฌํ˜„ ์™„๋ฃŒ์ผ:** 2025-09-09 +**๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ:** 2025-09-09 +**์ž‘์„ฑ์ž:** Claude Code SuperClaude Framework \ No newline at end of file diff --git a/main.tf b/main.tf index 228c261..6b62be0 100644 --- a/main.tf +++ b/main.tf @@ -35,8 +35,8 @@ resource "aws_cloudfront_distribution" "main" { comment = "CloudFront distribution for ${var.project_name} - ${var.environment}" default_root_object = "index.html" - # Aliases (custom domain names) - Disabled for default certificate - # aliases = var.cloudfront_aliases + # Aliases (custom domain names) - Enable when ACM certificate is available + aliases = var.create_acm_certificate ? var.cloudfront_aliases : null # Default cache behavior default_cache_behavior { @@ -74,9 +74,12 @@ resource "aws_cloudfront_distribution" "main" { } } - # SSL/TLS certificate - Use CloudFront default certificate (temporary) + # SSL/TLS certificate - Use ACM certificate when available viewer_certificate { - cloudfront_default_certificate = true + acm_certificate_arn = var.create_acm_certificate ? aws_acm_certificate.main[0].arn : null + ssl_support_method = var.create_acm_certificate ? "sni-only" : null + minimum_protocol_version = var.create_acm_certificate ? "TLSv1.2_2021" : null + cloudfront_default_certificate = var.create_acm_certificate ? false : true } # Custom error responses diff --git a/outputs.tf b/outputs.tf index fb9dd8c..8212f7b 100644 --- a/outputs.tf +++ b/outputs.tf @@ -67,6 +67,17 @@ output "waf_web_acl_id" { value = var.enable_waf ? aws_wafv2_web_acl.cloudfront[0].id : null } +output "waf_blocked_ips_set_arn" { + description = "WAF Blocked IPs IP Set ARN" + value = var.enable_waf ? aws_wafv2_ip_set.blocked_ips[0].arn : null +} + +output "waf_blocked_ips_set_id" { + description = "WAF Blocked IPs IP Set ID" + value = var.enable_waf ? aws_wafv2_ip_set.blocked_ips[0].id : null +} + + # Origin Information output "origin_domain" { description = "Origin domain name" @@ -110,4 +121,16 @@ output "domain_validation_records" { value = dvo.resource_record_value } ] -} \ No newline at end of file +} + +# CrowdSec Integration Information +output "crowdsec_sync_command" { + description = "Command to synchronize CrowdSec with WAF" + value = "incus exec crowdsec -- /usr/local/bin/crowdsec-waf-sync sync" +} + +output "waf_ip_set_id" { + description = "WAF IP Set ID for CrowdSec integration" + value = var.enable_waf ? aws_wafv2_ip_set.blocked_ips[0].id : null +} + diff --git a/security.tf b/security.tf index 0a3bba0..d792ba4 100644 --- a/security.tf +++ b/security.tf @@ -83,6 +83,24 @@ resource "aws_security_group" "web" { } } +# IP Set for blocked IPs +resource "aws_wafv2_ip_set" "blocked_ips" { + provider = aws.us_east_1 # CloudFront WAF must be in us-east-1 + count = var.enable_waf ? 1 : 0 + name = "${var.project_name}-${var.environment}-blocked-ips" + description = "IP addresses to be blocked" + scope = "CLOUDFRONT" + ip_address_version = "IPV4" + + # Start with empty set - IPs can be added via AWS CLI or Console + addresses = [] + + tags = { + Name = "${var.project_name}-${var.environment}-blocked-ips" + } +} + + # WAF Web ACL for CloudFront (optional) resource "aws_wafv2_web_acl" "cloudfront" { provider = aws.us_east_1 # CloudFront WAF must be in us-east-1 @@ -94,10 +112,32 @@ resource "aws_wafv2_web_acl" "cloudfront" { allow {} } - # Rate limiting rule + # Block IPs in blocked list + rule { + name = "BlockedIPsRule" + priority = 1 + + action { + block {} + } + + statement { + ip_set_reference_statement { + arn = aws_wafv2_ip_set.blocked_ips[0].arn + } + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "BlockedIPsRule" + sampled_requests_enabled = true + } + } + + # Rate limiting rule (original setting restored) rule { name = "RateLimitRule" - priority = 1 + priority = 2 action { block {} @@ -120,7 +160,7 @@ resource "aws_wafv2_web_acl" "cloudfront" { # AWS Managed Rules - Core Rule Set rule { name = "AWSManagedRulesCommonRuleSet" - priority = 2 + priority = 3 override_action { none {} @@ -143,7 +183,7 @@ resource "aws_wafv2_web_acl" "cloudfront" { # AWS Managed Rules - Known Bad Inputs rule { name = "AWSManagedRulesKnownBadInputsRuleSet" - priority = 3 + priority = 4 override_action { none {} diff --git a/sync-crowdsec-to-waf.sh b/sync-crowdsec-to-waf.sh new file mode 100755 index 0000000..051cba5 --- /dev/null +++ b/sync-crowdsec-to-waf.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +# CrowdSec์—์„œ ์ฐจ๋‹จ๋œ IP๋ฅผ AWS WAF BlockedIPsRule์— ๋™๊ธฐํ™”ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ + +# ์„ค์ • +WAF_IP_SET_ID="c43ff364-f3e2-43c7-8462-8fae20599d8d" +WAF_IP_SET_NAME="aws-cf-dev-blocked-ips" +REGION="us-east-1" +SCOPE="CLOUDFRONT" + +# CrowdSec์—์„œ ํ˜„์žฌ ์ฐจ๋‹จ๋œ IP ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ +get_crowdsec_banned_ips() { + # CrowdSec CLI๋ฅผ ํ†ตํ•ด ์ฐจ๋‹จ๋œ IP ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ + if command -v cscli &> /dev/null; then + # ๋กœ์ปฌ CrowdSec์—์„œ ์ฐจ๋‹จ ๊ฒฐ์ • ๊ฐ€์ ธ์˜ค๊ธฐ + cscli decisions list -o json | jq -r '.[] | select(.type=="ban") | .value' | sort -u + else + # Incus ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์˜ CrowdSec์— ์ ‘๊ทผ + incus exec crowdsec -- cscli decisions list -o json | jq -r '.[] | select(.type=="ban") | .value' | sort -u + fi +} + +# AWS WAF IP Set์˜ ํ˜„์žฌ IP ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ +get_waf_current_ips() { + aws wafv2 get-ip-set \ + --scope $SCOPE \ + --id $WAF_IP_SET_ID \ + --name $WAF_IP_SET_NAME \ + --region $REGION \ + --query 'IPSet.Addresses[]' \ + --output text | tr '\t' '\n' | sed 's|/32||g' | sort -u +} + +# WAF IP Set ์—…๋ฐ์ดํŠธ +update_waf_ip_set() { + local ip_list="$1" + + # ํ˜„์žฌ lock token ๊ฐ€์ ธ์˜ค๊ธฐ + LOCK_TOKEN=$(aws wafv2 get-ip-set \ + --scope $SCOPE \ + --id $WAF_IP_SET_ID \ + --name $WAF_IP_SET_NAME \ + --region $REGION \ + --query 'LockToken' \ + --output text) + + # IP ์ฃผ์†Œ๋ฅผ CIDR ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ (๋‹จ์ผ IP๋Š” /32 ์ถ”๊ฐ€) + local cidr_list="" + if [ -n "$ip_list" ]; then + cidr_list=$(echo "$ip_list" | grep -v '^$' | sed 's|$|/32|g' | paste -sd, -) + fi + + echo "Updating WAF IP Set with IPs: $cidr_list" + + # WAF IP Set ์—…๋ฐ์ดํŠธ + aws wafv2 update-ip-set \ + --scope $SCOPE \ + --id $WAF_IP_SET_ID \ + --name $WAF_IP_SET_NAME \ + --addresses $cidr_list \ + --lock-token $LOCK_TOKEN \ + --region $REGION +} + +# ๋ฉ”์ธ ๋™๊ธฐํ™” ๋กœ์ง +sync_ips() { + echo "$(date): Starting CrowdSec to WAF IP sync..." + + # CrowdSec์—์„œ ์ฐจ๋‹จ๋œ IP ๊ฐ€์ ธ์˜ค๊ธฐ + crowdsec_ips=$(get_crowdsec_banned_ips) + echo "CrowdSec banned IPs: $(echo "$crowdsec_ips" | wc -l) IPs" + + # ํ˜„์žฌ WAF IP Set์˜ IP ๊ฐ€์ ธ์˜ค๊ธฐ + waf_ips=$(get_waf_current_ips) + echo "Current WAF IPs: $(echo "$waf_ips" | wc -l) IPs" + + # IP ๋ชฉ๋ก ๋น„๊ต + if [ "$crowdsec_ips" != "$waf_ips" ]; then + echo "IP lists differ, updating WAF..." + update_waf_ip_set "$crowdsec_ips" + echo "WAF IP Set updated successfully!" + else + echo "IP lists are already in sync" + fi + + echo "$(date): Sync completed" +} + +# ์‹คํ–‰ +sync_ips \ No newline at end of file diff --git a/variables.tf b/variables.tf index ed38111..379a3dc 100644 --- a/variables.tf +++ b/variables.tf @@ -156,4 +156,5 @@ variable "cloudfront_logs_prefix" { description = "Prefix for CloudFront logs in S3" type = string default = "cloudfront-logs/" -} \ No newline at end of file +} +