diff --git a/Dockerfile b/Dockerfile index cbdfe8474..56a2a1596 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ COPY .env .env RUN mkdir -p backend && \ mkdir -p /var/log/djangolog && \ + chmod 777 /var/log/djangolog && \ apt-get update && \ apt-get install bash && \ apt-get install -y python3-pip @@ -19,4 +20,4 @@ WORKDIR /backend COPY init.sh ./init.sh RUN chmod +x ./init.sh -ENTRYPOINT ["./init.sh"] \ No newline at end of file +ENTRYPOINT ["./init.sh"] diff --git a/backend/login/views.py b/backend/login/views.py index 642c5ca13..1c1f613b5 100644 --- a/backend/login/views.py +++ b/backend/login/views.py @@ -50,7 +50,7 @@ if settings.DEBUG: EMAIL_AUTH_URI = 'https://localhost:3000/auth' else: - EMAIL_AUTH_URI = 'https://localhost:443/auth' + EMAIL_AUTH_URI = BASE_URL + 'register' class OAuthLoginView(APIView): @@ -159,7 +159,7 @@ def get_email_auth_uri(self): class Intra42CallbackView(OAuthCallbackView): def get_email_auth_uri(self): - return BASE_URL + 'auth' + return BASE_URL + 'register' client_id = INTRA42_CLIENT_ID client_secret = INTRA42_CLIENT_SECRET token_api = INTRA42_TOKEN_API diff --git a/backend/src/choices.py b/backend/src/choices.py index ef0a882c4..db1ce4e50 100644 --- a/backend/src/choices.py +++ b/backend/src/choices.py @@ -41,9 +41,9 @@ ] GAME_SETTINGS_DICT = { - 'bar': { + 'bar': { 'width': 10, - 'height': 100, + 'height': 300, 'speed': 30 }, 'ball': { diff --git a/backend/src/jwt_authentication.py b/backend/src/jwt_authentication.py index 3076fafa9..d7fcea9e0 100644 --- a/backend/src/jwt_authentication.py +++ b/backend/src/jwt_authentication.py @@ -5,7 +5,7 @@ from channels.middleware import BaseMiddleware from django.contrib.auth.models import AnonymousUser -SECRET_KEY = settings.SECRET_KEY +SECRET_KEY = settings.JWT_AUTH_SECRET_KEY class JWTAuthMiddleware(BaseMiddleware): diff --git a/backend/src/settings.py b/backend/src/settings.py index 6845c5236..97aebf2d0 100644 --- a/backend/src/settings.py +++ b/backend/src/settings.py @@ -118,11 +118,13 @@ def wait_for_vault_client(client, retries=5, delay=5): 'loggers': { '': { 'handlers': ['console', 'file'], + # 'handlers': ['console'], 'level': 'INFO', 'propagate': True, }, 'django': { 'handlers': ['console', 'file'], + # 'handlers': ['console'], 'level': 'INFO', 'propagate': True, }, @@ -317,4 +319,4 @@ def wait_for_vault_client(client, retries=5, delay=5): # Assets Root FRONTEND_ROOT = Path(BASE_DIR).parent / 'frontend' MEDIA_ROOT = os.path.join(FRONTEND_ROOT, 'assets', 'images') -MEDIA_URL = '/images/' +MEDIA_URL = '/images/' \ No newline at end of file diff --git a/elk/logstash/pipeline/logstash.conf b/elk/logstash/pipeline/logstash.conf index 9d07e764b..59da4fd3a 100644 --- a/elk/logstash/pipeline/logstash.conf +++ b/elk/logstash/pipeline/logstash.conf @@ -24,21 +24,17 @@ filter { } mutate { remove_field => ["@version", "@timestamp", "host", "path"] + gsub => ["message", "\[시작\]\s*", ""] } } - if "nginx" in [tags] { - grok { - match => { "request" => "%{WORD:method} %{URIPATH:path} HTTP/%{NUMBER:httpversion}" } - } + if "nginx" in [tags] { mutate { - convert => { - "요청을 처리하는데 걸린 시간" => "float" - } + convert => { "요청에 걸린 시간" => "float" } } - if [요청을 처리하는데 걸린 시간] == 0 { + if [요청에 걸린 시간] <= 0.0 { drop { } } - } + } } output { diff --git a/frontend/src/constants/routeInfo.js b/frontend/src/constants/routeInfo.js index c43965bf8..6443dacbb 100644 --- a/frontend/src/constants/routeInfo.js +++ b/frontend/src/constants/routeInfo.js @@ -6,7 +6,6 @@ import MainHeader from "../header/mainHeader/header.js"; import WaitingRoom from "../pages/waiting-room/page.js"; import Login from "../pages/login/page.js"; import CustomGameList from "../pages/custom-game-list/page.js"; -import Auth from "../pages/auth/page.js"; import LocalGame from "../pages/local-game/page.js"; import Matchup from "../pages/match-up/page.js"; import Summary from "../pages/histories/summary-page.js"; @@ -21,7 +20,6 @@ import LocalMatchup from "../pages/local-match-up/page.js"; */ export const routes = [ { path: /^\/$/, page: Login, header: emptyHeader }, - { path: /^\/auth(?:\?.*)?$/, page: Auth, header: emptyHeader }, { path: /^\/register$/, page: Register, header: emptyHeader }, { path: /^\/game-mode$/, page: GameMode, header: MainHeader }, diff --git a/frontend/src/header/mainHeader/header.js b/frontend/src/header/mainHeader/header.js index 5b9a1dc33..b7b0cc193 100644 --- a/frontend/src/header/mainHeader/header.js +++ b/frontend/src/header/mainHeader/header.js @@ -12,7 +12,6 @@ import { getUserMe } from "../../utils/userUtils.js"; */ export default function MainHeader($container) { this.$container = $container; - const init = () => { getUserMe().then((response) => { if (response.status === 200) { @@ -264,14 +263,15 @@ export default function MainHeader($container) { `, ); }; - this.renderFriendsList = () => { // 상태 관리 시스템으로부터 현재 친구 목록 상태를 가져옴. - const newFriendList = getFriendsList().length === undefined ? null : getFriendsList(); + const tempFriendList = getFriendsList(); + // friends가 배열인지 확인하고, 아니라면 빈 배열을 사용. + const newFriendList = Array.isArray(tempFriendList.friends) ? tempFriendList.friends : []; // 새로운 친구 목록을 기반으로 친구 카드를 생성. const newFriendCards = newFriendList?.slice(0, 8) - ?.map((card, index) => + .map((card, index) => createInfoCard( card, index, @@ -279,13 +279,13 @@ export default function MainHeader($container) { { iconImagePath: "../../assets/images/trash.png" }, ), ) - ?.join(""); + .join(""); const friendsListWrapper = document.getElementById("friends-list-wrapper"); if (friendsListWrapper !== null) { friendsListWrapper.innerHTML = `
- 친구 (${newFriendList === null ? 0 : newFriendList.friends.length} / 8) + 친구 (${newFriendList.length} / 8)
${newFriendCards} @@ -297,7 +297,7 @@ export default function MainHeader($container) { // 친구삭제 클릭 이벤트 - newFriendList?.forEach((friend, index) => { + newFriendList.forEach((friend, index) => { const iconElement = document.getElementById(`delete-icon-${index}`); if (iconElement) { @@ -325,6 +325,7 @@ export default function MainHeader($container) { const friendsListDiv = document.getElementById("friends-list"); friendsListDiv.innerHTML = loadingHtml; // 로딩 스피너를 friends-list 내부에 갱신 } else { + // TODO => 에러 페이지로 이동 navigate("/"); } }); @@ -460,7 +461,6 @@ export default function MainHeader($container) { } }); }; - let [getUserInfo, setUserInfo] = useState({}, this, "render"); let [getFriendsList, setFriendsList] = useState( {}, diff --git a/frontend/src/pages/auth/page.js b/frontend/src/pages/auth/page.js deleted file mode 100644 index 75ba8ecd7..000000000 --- a/frontend/src/pages/auth/page.js +++ /dev/null @@ -1,20 +0,0 @@ -import { navigate } from '../../utils/navigate.js'; -/** - * @param {HTMLElement} $container - */ -export default function Auth($container) { - - const setState = () => { - // URL에서 JWT 토큰 파싱 - const urlParams = new URLSearchParams(window.location.search); - const token = urlParams.get('token'); // 'jwt'는 URL 파라미터의 키 이름 - - // 토큰을 세션 스토리지에 저장 - if (token) { - localStorage.setItem('jwtToken', token); - } - navigate('/register'); - }; - - setState(); -} diff --git a/frontend/src/pages/local-game/page.js b/frontend/src/pages/local-game/page.js index f3b01142b..0c7d36c86 100644 --- a/frontend/src/pages/local-game/page.js +++ b/frontend/src/pages/local-game/page.js @@ -19,7 +19,7 @@ export default function LocalGame($container, info = null) { let scoreInput = { player1: 0, player2: 0 }; let [getScore, setScore] = useState(scoreInput, this, "renderScoreBoard"); let [getTime, setTime] = useState(0, this, "renderTime"); - const BALL_SPEED = 7; + let BALL_SPEED = 7; const init = () => { hideHeader(); @@ -125,6 +125,7 @@ export default function LocalGame($container, info = null) { const ctx = canvas.getContext("2d"); canvas.width = document.body.clientWidth; canvas.height = document.body.clientHeight * 0.88; // header의 height가 12vh이므로 88%만큼의 height를 가짐 + BALL_SPEED = canvas.height * 0.02; // 초기 위치 설정 let commonBarInfo = { diff --git a/frontend/src/pages/match-up/page.js b/frontend/src/pages/match-up/page.js index ebb11b79e..d5b98c677 100644 --- a/frontend/src/pages/match-up/page.js +++ b/frontend/src/pages/match-up/page.js @@ -9,7 +9,13 @@ export default function Matchup($container, info = null) { } // 이전 페이지로 부터 받아온 정보 처리 const ws = info.socket; - ws.onmessage = null; + ws.onmessage = (msg) => { + ws.close() + let data = JSON.parse(msg.data); + if (data.type === "close.connection") { + navigate("/game-mode"); + } + } // ws.onmessage = (msg) => { // let data = JSON.parse(msg.data); // let matchData = []; @@ -126,6 +132,10 @@ export default function Matchup($container, info = null) { }; const renderFinal = () => { + const $semiFinalCssLink = document.querySelector( + 'link[href*="semi-final.css"]', + ); + $semiFinalCssLink?.parentNode.removeChild($semiFinalCssLink); importCss("../../../assets/css/final.css"); $container.innerHTML = ` diff --git a/frontend/src/pages/online-game/page.js b/frontend/src/pages/online-game/page.js index e6e62e248..1fa3abbbb 100644 --- a/frontend/src/pages/online-game/page.js +++ b/frontend/src/pages/online-game/page.js @@ -59,12 +59,12 @@ export default function OnlineGame($container, info) { canvas.width = document.body.clientWidth; canvas.height = document.body.clientHeight * 0.88; // header의 height가 12vh이므로 88%만큼의 height를 가짐 - bar1 = { x: 10, y: canvas.height / 2 - 50, width: 20, height: 100 }; + bar1 = { x: 10, y: canvas.height / 2 - 50, width: 10, height: 300 }; bar2 = { x: canvas.width - 30, y: canvas.height / 2 - 50, - width: 20, - height: 100, + width: 10, + height: 300, }; ball = { x: canvas.width / 2, y: canvas.height / 2, radius: 10 }; ws.send( @@ -83,12 +83,12 @@ export default function OnlineGame($container, info) { if (data.type === "in_game") { bar1.x = data.data.left_side_player.x; bar1.y = data.data.left_side_player.y; - bar1.width = data.data.width; - bar1.height = data.data.height; + bar1.width = 10; + bar1.height = 300; bar2.x = data.data.right_side_player.x; bar2.y = data.data.right_side_player.y; - bar2.width = data.data.width; - bar2.height = data.data.height; + bar2.width = 10; + bar2.height = 300; ball.x = data.data.ball.x; ball.y = data.data.ball.y; draw(bar1, bar2, ball); @@ -103,6 +103,7 @@ export default function OnlineGame($container, info) { ) setScore(newScore); } else if (data.type === "game_end") endGame(data, ws); + else if (data.type === "close.connection") navigate("/game-mode"); }; }; @@ -225,6 +226,10 @@ export default function OnlineGame($container, info) { ws.send(JSON.stringify({ type: "match3_info" })); ws.onmessage = (msg) => { let data = JSON.parse(msg.data); + if (data.type === "close.connection") { + navigate("/game-mode"); + return ; + } navigate(`/match-up`, { socket: ws, data: data, remainMatch: true }); }; } @@ -244,7 +249,11 @@ export default function OnlineGame($container, info) { } else { ws.onmessage = (msg) => { const data = JSON.parse(msg.data); - if (data.type === "game_end") { + if (data.type === "close.connection") { + navigate("/game-mode"); + return ; + } + else if (data.type === "game_end") { match3Logic(ws); } }; @@ -264,4 +273,4 @@ export default function OnlineGame($container, info) { ); } } -} +} \ No newline at end of file diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 608730828..7193fdba2 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -3,32 +3,32 @@ FROM nginx:1.21.6-alpine # 필요한 패키지 설치 RUN apk add --no-cache git build-base libtool automake autoconf zlib-dev pcre-dev openssl-dev linux-headers openssl -# # ModSecurity 다운로드 및 컴파일 -# RUN git clone --depth 1 https://github.com/SpiderLabs/ModSecurity.git /usr/local/src/modsecurity \ -# && cd /usr/local/src/modsecurity \ -# && git submodule init \ -# && git submodule update \ -# && ./build.sh \ -# && ./configure \ -# && make \ -# && make install +# ModSecurity 다운로드 및 컴파일 +RUN git clone --depth 1 https://github.com/SpiderLabs/ModSecurity.git /usr/local/src/modsecurity \ + && cd /usr/local/src/modsecurity \ + && git submodule init \ + && git submodule update \ + && ./build.sh \ + && ./configure \ + && make \ + && make install # ModSecurity-nginx 커넥터 다운로드 -# RUN git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git /usr/local/src/modsecurity-nginx +RUN git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git /usr/local/src/modsecurity-nginx # Nginx 컴파일을 위한 준비 및 ModSecurity 모듈 빌드 -# ARG NGINX_VERSION=1.21.6 -# RUN wget https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz \ -# && tar zxvf nginx-${NGINX_VERSION}.tar.gz \ -# && cd nginx-${NGINX_VERSION} \ -# && ./configure --with-compat --add-dynamic-module=/usr/local/src/modsecurity-nginx \ -# && make modules \ -# && cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules +ARG NGINX_VERSION=1.21.6 +RUN wget https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz \ + && tar zxvf nginx-${NGINX_VERSION}.tar.gz \ + && cd nginx-${NGINX_VERSION} \ + && ./configure --with-compat --add-dynamic-module=/usr/local/src/modsecurity-nginx \ + && make modules \ + && cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules # ModSecurity 설정 디렉토리 생성 및 파일 복사 -# RUN mkdir -p /etc/nginx/modsecurity -# COPY ./config/modsecurity.conf /etc/nginx/modsecurity/ -# COPY ./config/owasp-crs /etc/nginx/modsecurity/owasp-crs +RUN mkdir -p /etc/nginx/modsecurity +COPY ./config/modsecurity.conf /etc/nginx/modsecurity/ +COPY ./config/owasp-crs /etc/nginx/modsecurity/owasp-crs RUN apk add --no-cache nss-tools RUN wget https://github.com/FiloSottile/mkcert/releases/download/v1.4.3/mkcert-v1.4.3-linux-amd64 -O /usr/local/bin/mkcert @@ -36,14 +36,14 @@ RUN chmod +x /usr/local/bin/mkcert RUN mkcert -install RUN mkdir -p /etc/nginx/certs -RUN mkcert -key-file /etc/nginx/certs/server.key -cert-file /etc/nginx/certs/server.crt "hashicorp_vault" "django_node_app" "prometheus" "grafana" "alertmanager" localhost 127.0.0.1 ::1 +RUN mkcert -key-file /etc/nginx/certs/server.key -cert-file /etc/nginx/certs/server.crt "hashicorp_vault" "django_node_app" "prometheus" "grafana" "alertmanager" localhost 127.0.0.1 ::1 10.14.10.2 # Nginx 설정 파일 복사 및 모듈 로드 COPY ./config/nginx.conf /etc/nginx/nginx.conf -# RUN mkdir -p /etc/nginx/modules-load.d/ \ -# && echo 'load_module /usr/lib/nginx/modules/ngx_http_modsecurity_module.so;' > /etc/nginx/modules-load.d/00-modsecurity.conf \ -# && cat /etc/nginx/nginx.conf > /etc/nginx/nginx.conf.bak \ -# && cat /etc/nginx/modules-load.d/00-modsecurity.conf /etc/nginx/nginx.conf.bak > /etc/nginx/nginx.conf +RUN mkdir -p /etc/nginx/modules-load.d/ \ + && echo 'load_module /usr/lib/nginx/modules/ngx_http_modsecurity_module.so;' > /etc/nginx/modules-load.d/00-modsecurity.conf \ + && cat /etc/nginx/nginx.conf > /etc/nginx/nginx.conf.bak \ + && cat /etc/nginx/modules-load.d/00-modsecurity.conf /etc/nginx/nginx.conf.bak > /etc/nginx/nginx.conf -CMD ["nginx", "-g", "daemon off;"] +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/nginx/config/modsecurity.conf b/nginx/config/modsecurity.conf index ec436ac4d..f04f120a4 100644 --- a/nginx/config/modsecurity.conf +++ b/nginx/config/modsecurity.conf @@ -3,14 +3,21 @@ Include /etc/nginx/modsecurity/owasp-crs/crs-setup.conf Include /etc/nginx/modsecurity/owasp-crs/rules/*.conf SecRuleEngine On +# 규칙 엔진 활성화 / 요청 및 응답에 대한 검사 수행 SecRequestBodyAccess On +# 요청 본문에 대한 접근 허용 / POST와 같은 요청등의 본문 내용 검사 SecResponseBodyAccess On +# 접근 허용 및 웹 애플리케이션으로부터의 응답 내용 분석 SecResponseBodyMimeType text/plain text/html text/xml +# 응답 본문의 MIME type 지정 SecDataDir /var/cache/modsecurity +# 세션 데이터 및 임시 파일 저장소 SecTmpDir /tmp +# 임시 파일 저장소 SecAuditLogType Serial +# 감사 로그의 유형을 시리얼로 정하고, 이를 차곡차곡 쌓아둘 것이다. SecAuditLog /var/log/nginx/modsec_audit.log SecDebugLog /var/log/nginx/modsec_debug.log SecDebugLogLevel 0 SecDefaultAction "phase:1,log,auditlog,pass" -SecDefaultAction "phase:2,log,auditlog,pass" +SecDefaultAction "phase:2,log,auditlog,pass" \ No newline at end of file diff --git a/nginx/config/nginx.conf b/nginx/config/nginx.conf index c4b7655fd..99834edb1 100644 --- a/nginx/config/nginx.conf +++ b/nginx/config/nginx.conf @@ -11,24 +11,34 @@ http { include /etc/nginx/mime.types; default_type application/octet-stream; - log_format json_format escape=json - '{' - '"요청에 대한 응답 코드": $status, ' - '"클라이언트에게 전송된 응답 메세지 바이트 수": $body_bytes_sent, ' + log_format json_combined escape=json + '{' + '"time_local": "$time_local", ' + '"remote_addr": "$remote_addr", ' + '"remote_user": "$remote_user", ' + '"request": "$request", ' + '"status": $status, ' + '"body_bytes_sent": $body_bytes_sent, ' + '"요청에 걸린 시간": $request_time, ' + '"http_referrer": "$http_referer", ' + '"http_user_agent": "$http_user_agent", ' + '"http_x_forwarded_for": "$http_x_forwarded_for", ' '"request_method": "$request_method", ' - '"요청을 처리하는데 걸린 시간": "$request_time", ' - '}'; + '"http_host": "$host", ' + '"server_protocol": "$server_protocol", ' + '"upstream_response_time": "$upstream_response_time", ' + '"upstream_addr": "$upstream_addr"' + '}'; - access_log /var/log/nginx/access_json.log json_format; + access_log /var/log/nginx/access_json.log json_combined; error_log /var/log/nginx/error.log warn; sendfile on; - keepalive_timeout 65; - # modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf; - # modsecurity on; + modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf; + modsecurity on; # Django 애플리케이션에 대한 설정 @@ -90,4 +100,4 @@ http { access_log off; } } -} \ No newline at end of file +} diff --git a/nginx/config/owasp-crs/crs-setup.conf b/nginx/config/owasp-crs/crs-setup.conf index 8c808d107..8b86cca38 100644 --- a/nginx/config/owasp-crs/crs-setup.conf +++ b/nginx/config/owasp-crs/crs-setup.conf @@ -1,5 +1,3 @@ - - # SecDefaultAction "phase:1,log,auditlog,pass" # SecDefaultAction "phase:2,log,auditlog,pass" @@ -13,7 +11,7 @@ # SecDefaultAction "phase:2,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'" -#SecAction \ +# SecAction \ # "id:900000,\ # phase:1,\ # pass,\ @@ -21,7 +19,7 @@ # nolog,\ # setvar:tx.blocking_paranoia_level=1" -#SecAction \ +# SecAction \ # "id:900001,\ # phase:1,\ # pass,\ @@ -29,7 +27,7 @@ # nolog,\ # setvar:tx.detection_paranoia_level=1" -#SecAction \ +# SecAction \ # "id:900010,\ # phase:1,\ # pass,\ @@ -38,7 +36,7 @@ # setvar:tx.enforce_bodyproc_urlencoded=1" -#SecAction \ +# SecAction \ # "id:900100,\ # phase:1,\ # pass,\ @@ -49,7 +47,7 @@ # setvar:tx.warning_anomaly_score=3,\ # setvar:tx.notice_anomaly_score=2" -#SecAction \ +# SecAction \ # "id:900110,\ # phase:1,\ # pass,\ @@ -58,7 +56,7 @@ # setvar:tx.inbound_anomaly_score_threshold=5,\ # setvar:tx.outbound_anomaly_score_threshold=4" -#SecAction \ +# SecAction \ # "id:900115,\ # phase:1,\ # pass,\ @@ -66,7 +64,7 @@ # nolog,\ # setvar:tx.reporting_level=4" -#SecAction \ +# SecAction \ # "id:900120,\ # phase:1,\ # pass,\ @@ -75,7 +73,7 @@ # setvar:tx.early_blocking=1" -#SecAction \ +# SecAction \ # "id:900130,\ # phase:1,\ # pass,\ @@ -84,7 +82,7 @@ # setvar:tx.enable_default_collections=1" -#SecAction \ +# SecAction \ # "id:900200,\ # phase:1,\ # pass,\ @@ -100,4 +98,12 @@ SecAction \ pass,\ t:none,\ nolog,\ - setvar:tx.crs_setup_version=400" \ No newline at end of file + setvar:tx.crs_setup_version=400" + +SecAction \ + "id:900200,\ + phase:1,\ + pass,\ + t:none,\ + nolog,\ + setvar:'tx.allowed_methods=GET HEAD POST OPTIONS PATCH'" \ No newline at end of file