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