From 4762372e42afc78c36307be32aa604821b602419 Mon Sep 17 00:00:00 2001 From: Yoonchulchung Date: Mon, 23 Feb 2026 19:08:16 +0900 Subject: [PATCH] =?UTF-8?q?feat(#407):=20HAProxy=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 86 +++++++---------------------------- haproxy/fail2ban-haproxy.conf | 14 ++++++ haproxy/haproxy-log.service | 12 +++++ haproxy/haproxy.cfg | 57 ++++++++++++++++++++++- haproxy/iptables-docker.conf | 16 +++++++ haproxy/jail-haproxy.conf | 8 ++++ haproxy/logrotate-haproxy | 9 ++++ 7 files changed, 130 insertions(+), 72 deletions(-) create mode 100644 haproxy/fail2ban-haproxy.conf create mode 100644 haproxy/haproxy-log.service create mode 100644 haproxy/iptables-docker.conf create mode 100644 haproxy/jail-haproxy.conf create mode 100644 haproxy/logrotate-haproxy diff --git a/README.md b/README.md index 3d131475..6d4c631e 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ --- -## ๐ŸŽฏ Real Match๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ•ต์‹ฌ ๊ธฐ๋Šฅ +## Real Match๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ•ต์‹ฌ ๊ธฐ๋Šฅ -- ๐Ÿ“Œ **๋งค์นญ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ธฐ๋ฐ˜ ์ถ”์ฒœ** -- ๐Ÿ“Š **๋งค์นญ ์ง„๋‹จ ๋Œ€์‹œ๋ณด๋“œ** -- ๐Ÿ“… **ํ˜‘์—… ์ƒํƒœ ์ถ”์  ๋ฐ ์ผ์ • ๊ด€๋ฆฌ ๋กœ์ง** -- ๐Ÿ’ฌ **์‹ค์‹œ๊ฐ„ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ๋ฐ ์•Œ๋ฆผ ์‹œ์Šคํ…œ** +- **๋งค์นญ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ธฐ๋ฐ˜ ์ถ”์ฒœ** +- **๋งค์นญ ์ง„๋‹จ ๋Œ€์‹œ๋ณด๋“œ** +- **ํ˜‘์—… ์ƒํƒœ ์ถ”์  ๋ฐ ์ผ์ • ๊ด€๋ฆฌ ๋กœ์ง** +- **์‹ค์‹œ๊ฐ„ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ๋ฐ ์•Œ๋ฆผ ์‹œ์Šคํ…œ** --- @@ -26,15 +26,15 @@ Real Match๋Š” ์œ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉ ์ œ๊ณตํ•˜์—ฌ ๋ธŒ๋žœ๋“œ์™€ ์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„ ๋‹จ์ˆœ ๋งค์นญ์„ ๋„˜์–ด, -> ๐Ÿ‘‰ **๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ๋ถ„์„ + ํ˜‘์—… ํ”„๋กœ์„ธ์Šค ํ†ตํ•ฉ ๊ด€๋ฆฌ** +> **ํƒœ๊ทธ ๊ธฐ๋ฐ˜ ๋งค์นญ + ์ฑ„ํŒ… ๊ธฐ๋ฐ˜ ํ˜‘์—ฝ ์ง„ํ–‰** ๋ฅผ ํ†ตํ•ด ๋ณด๋‹ค ์ •๊ตํ•˜๊ณ  ํšจ์œจ์ ์ธ ๋ธŒ๋žœ๋“œ ๋งˆ์ผ€ํŒ… ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. -## ๐Ÿงฉ ์ฃผ์š” ๊ธฐ๋Šฅ +## ์ฃผ์š” ๊ธฐ๋Šฅ | ๊ธฐ๋Šฅ | ์„ค๋ช… | |------------------|-----------------------------| -| ๐Ÿ“ **๋งค์นญ ๊ฒ€์‚ฌ** | ๋งค์นญ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ธฐ๋ฐ˜ ๋ธŒ๋žœ๋“œ ์ถ”์ฒœ | +| ๐Ÿ“ **๋งค์นญ ๊ฒ€์‚ฌ** | ๋งค์นญ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ธฐ๋ฐ˜ ๋ธŒ๋žœ๋“œ ๋ฐ ์บ ํŽ˜์ธ ์ถ”์ฒœ | | ๐Ÿ”— **๋งค์นญ ์ง„๋‹จ ๋Œ€์‹œ๋ณด๋“œ** | ๋ธŒ๋žœ๋“œ ยท ์ธํ”Œ๋ฃจ์–ธ์„œ๊ฐ„์˜ ๋งค์นญ ํ˜„ํ™ฉ ๊ด€๋ฆฌ | | ๐Ÿ“† **ํ˜‘์—… ์ผ์ • ๊ด€๋ฆฌ** | ์ผ์ • ๋ฐ ํ˜‘์—… ๊ด€๋ฆฌ ์บ˜๋ฆฐ๋” | | ๐Ÿ“† **์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… ๊ธฐ๋Šฅ** | ๋ธŒ๋žœ๋“œยท์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„ ์‹ค์‹œ๊ฐ„ ๋ฉ”์‹œ์ง€ ๋ฐ ํŒŒ์ผ ๊ณต์œ  | @@ -43,16 +43,16 @@ Real Match๋Š” ์œ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉ ์ œ๊ณตํ•˜์—ฌ ๋ธŒ๋žœ๋“œ์™€ ์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„ | ๐Ÿ‘ค **ํšŒ์› ๊ด€๋ฆฌ** | JWT ๊ธฐ๋ฐ˜ ํšŒ์›๊ฐ€์ž… / ์†Œ์…œ ๋กœ๊ทธ์ธ | | ๐Ÿ–ผ๏ธ **ํ”„๋กœํ•„ ๊ด€๋ฆฌ** | ๋งˆ์ดํŽ˜์ด์ง€์—์„œ ํ”„๋กœํ•„ ๋ฐ ๊ด€์‹ฌ์‚ฌ ์ˆ˜์ • | ---- -## ๐Ÿ› ์•„ํ‚คํ…์ณ ๊ตฌ์กฐ + +## ์•„ํ‚คํ…์ณ ๊ตฌ์กฐ ![img_3.png](docs/images/img_3.png) --- -## ๐Ÿ‘ฉโ€๐Ÿ’ป RealMatch Spring Developers +## RealMatch Spring Developers
@@ -65,15 +65,7 @@ Real Match๋Š” ์œ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉ ์ œ๊ณตํ•˜์—ฌ ๋ธŒ๋žœ๋“œ์™€ ์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„
-## ๐Ÿ‘ฅ Detailed Responsibilities -

-๐Ÿ–ฑ๏ธ ์ขŒ์šฐ๋กœ ์Šคํฌ๋กคํ•ด์„œ ๋‚ด์šฉ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”. -

-
- -
- - +## Detailed Responsibilities ### ๐Ÿ‘จโ€๐Ÿ’ป ๊ณ ๊ฒฝ์ˆ˜ **Backend Developer** @@ -83,10 +75,6 @@ Real Match๋Š” ์œ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉ ์ œ๊ณตํ•˜์—ฌ ๋ธŒ๋žœ๋“œ์™€ ์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„ - ์‚ฌ์šฉ์ž ์ •๋ณด ๊ด€๋ฆฌ ๋ฐ ๋งˆ์ดํŽ˜์ด์ง€ API ๊ตฌํ˜„ - Role ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ์ œ์–ด ์ฒ˜๋ฆฌ -
- -
- ### ๐Ÿ‘ฉโ€๐Ÿ’ป ๋ฐ•์ง€์˜ **Backend Developer / API Lead** @@ -96,10 +84,6 @@ Real Match๋Š” ์œ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉ ์ œ๊ณตํ•˜์—ฌ ๋ธŒ๋žœ๋“œ์™€ ์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„ - ๋ธŒ๋žœ๋“œ-์บ ํŽ˜์ธ ์กฐํšŒ ๋ฐ ์ข‹์•„์š” ๊ธฐ๋Šฅ ๊ตฌํ˜„ - Docker ๊ธฐ๋ฐ˜ CD ๊ตฌ์ถ• ๋ฐ ์„œ๋ฒ„ ๋ฐฐํฌ ์ž๋™ํ™” -
- -
- ### ๐Ÿ‘ฉโ€๐Ÿ’ป ์—ฌ์ฑ„ํ˜„ **Backend Developer** @@ -108,10 +92,6 @@ Real Match๋Š” ์œ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉ ์ œ๊ณตํ•˜์—ฌ ๋ธŒ๋žœ๋“œ์™€ ์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„ - RabbitMQ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ์•Œ๋ฆผ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ - Firebase ยท Email ์—ฐ๋™ ์•Œ๋ฆผ ์‹œ์Šคํ…œ ๊ตฌํ˜„ -
- -
- ### ๐Ÿ‘ฉโ€๐ŸŽจ ์ด์˜ˆ๋ฆผ **Backend Developer** @@ -119,10 +99,6 @@ Real Match๋Š” ์œ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉ ์ œ๊ณตํ•˜์—ฌ ๋ธŒ๋žœ๋“œ์™€ ์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„ - ๋ธŒ๋žœ๋“œ ๋ชฉ๋ก ยท ์ƒ์„ธ ยท ์š”์•ฝ ์กฐํšŒ API ๊ฐœ๋ฐœ - ๋ธŒ๋žœ๋“œ ์ข‹์•„์š” ํ† ๊ธ€ ๋ฐ ํ˜‘์ฐฌ ์ œํ’ˆ ์กฐํšŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„ -
- -
- ### ๐Ÿ‘จโ€๐Ÿ’ป ์ •์œค์ฒ  **Backend Developer / Algorithm Lead** @@ -130,38 +106,9 @@ Real Match๋Š” ์œ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ฉ ์ œ๊ณตํ•˜์—ฌ ๋ธŒ๋žœ๋“œ์™€ ์ธํ”Œ๋ฃจ์–ธ์„œ ๊ฐ„ - ๋งค์นญ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ธฐ๋ฐ˜ ์ถ”์ฒœ API ์„ค๊ณ„ ๋ฐ ๊ตฌํ˜„ - Redis ์บ์‹ฑ์„ ํ™œ์šฉํ•œ ์กฐํšŒ ์„ฑ๋Šฅ ๊ฐœ์„  - Docker ๊ธฐ๋ฐ˜ ์„œ๋ฒ„ ํ™˜๊ฒฝ ๊ตฌ์„ฑ ๋ฐ CI ์ž๋™ํ™” ๊ตฌ์ถ• +- ์ฝ”๋“œ ์ปจ๋ฒค์…˜ ๊ด€๋ฆฌ +- HAProxy๋ฅผ ์ด์šฉํ•œ ์„œ๋ฒ„ ๋ณด์•ˆ ๊ด€๋ฆฌ -
- -
- ---- - -## ๐Ÿ“‚ Project Structure - -```yml -BE -โ”œโ”€โ”€ global (๊ณตํ†ต ์„ค์ •, ์˜ˆ์™ธ ์ฒ˜๋ฆฌ, ์œ ํ‹ธ ๋“ฑ) -โ””โ”€โ”€ user - โ”œโ”€โ”€ presentation (์™ธ๋ถ€์™€์˜ ์ ‘์ ) - โ”‚ โ”œโ”€โ”€ UserController.java - โ”‚ โ””โ”€โ”€ dto (ํ•ด๋‹น ๋„๋ฉ”์ธ ์ „์šฉ ๋ฐ์ดํ„ฐ ์ „์†ก ๊ฐ์ฒด) - โ”‚ โ”œโ”€โ”€ request - โ”‚ โ”‚ โ””โ”€โ”€ UserRequest.java - โ”‚ โ””โ”€โ”€ response - โ”‚ โ””โ”€โ”€ UserResponse.java - โ”œโ”€โ”€ application (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ํ๋ฆ„ ์ œ์–ด) - โ”‚ โ””โ”€โ”€ service - โ”‚ โ”œโ”€โ”€ UserService.java (Interface) - โ”‚ โ””โ”€โ”€ UserServiceImpl.java - โ””โ”€โ”€ domain (ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™ ๋ฐ ์—”ํ‹ฐํ‹ฐ) - โ”œโ”€โ”€ entity - โ”‚ โ””โ”€โ”€ UserEntity.java - โ””โ”€โ”€ repository - โ””โ”€โ”€ UserRepository.java - -``` ---- ## ๐Ÿ›  Tech Stack @@ -178,16 +125,15 @@ BE ### Infra - Docker / Docker Compose -- AWS EC2 -- AWS RDS +- Gabia Cloud - AWS S3 / CloudFront +- HAProxy ### Communication - WebSocket (์ฑ„ํŒ…) - Firebase / Email (์•Œ๋ฆผ) - RabbitMQ ---- ## ๐Ÿ“ ์ฝ”๋“œ ์ปจ๋ฒค์…˜ diff --git a/haproxy/fail2ban-haproxy.conf b/haproxy/fail2ban-haproxy.conf new file mode 100644 index 00000000..89d09b1f --- /dev/null +++ b/haproxy/fail2ban-haproxy.conf @@ -0,0 +1,14 @@ +[Definition] + +# ๋กœ๊ทธ ํ˜•์‹: 2026-02-15T17:06:02.613Z IP:PORT [haproxy_date] frontend backend/server timers status ... +# docker logs --timestamps ๊ฐ€ ISO8601 ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ์ค„ ์‹œ์ž‘์— ์ถ”๊ฐ€ +# fail2ban์ด ํƒ€์ž„์Šคํƒฌํ”„ ์ œ๊ฑฐ ํ›„ ๊ณต๋ฐฑ์ด ๋‚จ์œผ๋ฏ€๋กœ ^\s* ์‚ฌ์šฉ + +# 403 ๊ฑฐ๋ถ€ (๋ธ”๋ž™๋ฆฌ์ŠคํŠธ, ACL ๊ฑฐ๋ถ€) +# 429 Rate Limit ์ดˆ๊ณผ +# ์ทจ์•ฝ์  ์Šค์บ” ํŒจํ„ด (.php, cgi-bin, .env, wp-admin, .git, shell, passwd ๋“ฑ) +failregex = ^\s*:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+403\s + ^\s*:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+429\s + ^\s*:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+\d+\s.*"(GET|POST|PUT|DELETE|HEAD|OPTIONS)\s+.*(\.php|cgi-bin|\.env|wp-admin|wp-login|\.git|shell|passwd|\.asp) + +ignoreregex = diff --git a/haproxy/haproxy-log.service b/haproxy/haproxy-log.service new file mode 100644 index 00000000..ea8969a9 --- /dev/null +++ b/haproxy/haproxy-log.service @@ -0,0 +1,12 @@ +[Unit] +Description=Stream HAProxy Docker logs to file +After=docker.service + +[Service] +Type=simple +ExecStart=/bin/bash -c '/usr/bin/docker logs -f --timestamps --tail=0 haproxy 2>&1 | sed -u "s/\([0-9]\{3\}\)[0-9]*Z/\1Z/" >> /var/log/haproxy/access.log' +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/haproxy/haproxy.cfg b/haproxy/haproxy.cfg index 293fc8cc..bb49d376 100644 --- a/haproxy/haproxy.cfg +++ b/haproxy/haproxy.cfg @@ -3,26 +3,79 @@ global maxconn 4096 daemon + # ์„œ๋ฒ„ ์ •๋ณด ์ˆจ๊ธฐ๊ธฐ + tune.h2.max-concurrent-streams 128 + defaults log global mode http option httplog option dontlognull + option forwardfor timeout connect 5000ms timeout client 50000ms timeout server 50000ms timeout http-request 10s timeout http-keep-alive 2s + # ๊ธฐ๋ณธ ์—๋Ÿฌ ํŽ˜์ด์ง€์—์„œ HAProxy ๋ฒ„์ „ ์ˆจ๊ธฐ๊ธฐ + errorfile 403 /usr/local/etc/haproxy/errors/403.http + frontend http_in bind *:80 - # HTTP๋ฅผ HTTPS๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ + + acl blacklisted src -f /usr/local/etc/haproxy/blacklist.lst + http-request deny if blacklisted + + stick-table type ip size 100k expire 30s store http_req_rate(10s) + http-request track-sc0 src + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 50 } + http-request redirect scheme https unless { ssl_fc } frontend https_in - bind *:443 ssl crt /etc/ssl/certs/realmatch.pem + bind *:443 ssl crt /etc/ssl/certs/realmatch.pem alpn h2,http/1.1 ssl-min-ver TLSv1.2 ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305 + + acl blacklisted src -f /usr/local/etc/haproxy/blacklist.lst + http-request deny if blacklisted + + stick-table type ip size 100k expire 30s store http_req_rate(10s) + http-request track-sc0 src + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 50 } + + acl valid_host hdr(host) -i api.realmatch.co.kr + http-request deny deny_status 403 unless valid_host + + http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + http-response set-header X-Frame-Options "SAMEORIGIN" + http-response set-header X-Content-Type-Options "nosniff" + http-response set-header X-XSS-Protection "1; mode=block" + http-response set-header Referrer-Policy "strict-origin-when-cross-origin" + http-response set-header Permissions-Policy "camera=(), microphone=(), geolocation=()" + + http-response del-header Server + http-response del-header X-Powered-By + + http-request deny if { req.hdr_cnt(content-length) gt 1 } + default_backend spring_servers +listen stats + bind *:8404 + stats enable + stats uri /stats + stats refresh 5s + stats show-legends + stats show-node + stats auth admin:realmatch2026! + stats admin if TRUE + +frontend prometheus + bind *:8405 + mode http + http-request use-service prometheus-exporter if { path /metrics } + no log + backend spring_servers balance roundrobin option httpchk GET /actuator/health diff --git a/haproxy/iptables-docker.conf b/haproxy/iptables-docker.conf new file mode 100644 index 00000000..6e5a24da --- /dev/null +++ b/haproxy/iptables-docker.conf @@ -0,0 +1,16 @@ +# Fail2ban action for Docker (uses DOCKER-USER chain) +[Definition] + +actionstart = iptables -N f2b- 2>/dev/null + iptables -A f2b- -j RETURN + iptables -I DOCKER-USER -j f2b- + +actionstop = iptables -D DOCKER-USER -j f2b- + iptables -F f2b- + iptables -X f2b- + +actioncheck = iptables -n -L DOCKER-USER | grep -q 'f2b-' + +actionban = iptables -I f2b- 1 -s -j DROP + +actionunban = iptables -D f2b- -s -j DROP diff --git a/haproxy/jail-haproxy.conf b/haproxy/jail-haproxy.conf new file mode 100644 index 00000000..ca8ac148 --- /dev/null +++ b/haproxy/jail-haproxy.conf @@ -0,0 +1,8 @@ +[haproxy-attack] +enabled = true +filter = haproxy-attack +logpath = /var/log/haproxy/access.log +action = iptables-docker[name=haproxy] +maxretry = 3 +findtime = 300 +bantime = 3600 diff --git a/haproxy/logrotate-haproxy b/haproxy/logrotate-haproxy new file mode 100644 index 00000000..450752be --- /dev/null +++ b/haproxy/logrotate-haproxy @@ -0,0 +1,9 @@ +/var/log/haproxy/access.log { + daily + rotate 14 + compress + delaycompress + missingok + notifempty + copytruncate +}