233 lines
10 KiB
Plaintext
233 lines
10 KiB
Plaintext
# ===============================================
|
|
# Production Level Nginx Configuration (Sample Template)
|
|
# 서버 사양: 쿼드코어 CPU, 4GB RAM, ~50 req/s
|
|
# 백엔드: FastAPI REST API Server
|
|
# ===============================================
|
|
|
|
# HTTP 서버 블록 (포트 80) - HTTPS로 리다이렉트
|
|
server {
|
|
listen 80;
|
|
server_name domain www.domain;
|
|
|
|
# 보안을 위한 호스트 검증 - 허용되지 않은 도메인 차단
|
|
# 도메인 확장자가 다르다면 추가해줘야함
|
|
if ($host !~* ^(www\.)?domain\.(com|kr|net|org)$) {
|
|
return 444;
|
|
}
|
|
|
|
# Let's Encrypt 도메인 검증 프로그램 허용 (리다이렉트 전에 처리)
|
|
# SSL 인증서 갱신을 위해 필수
|
|
location ^~ /.well-known/acme-challenge/ {
|
|
allow all;
|
|
root /www/certbot;
|
|
try_files $uri =404; # 디렉토리 순회 공격 방지
|
|
}
|
|
|
|
# HTTP를 HTTPS로 리다이렉트 (acme-challenge 제외)
|
|
# return 301은 rewrite보다 효율적이며 $server_name이 $host보다 안전함
|
|
location / {
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
}
|
|
|
|
# HTTPS 서버 블록 (포트 443) - 메인 애플리케이션
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name domain.com www.domain.com;
|
|
|
|
# 악성 봇 차단 (nginx.conf의 http 블록에서 $bad_bot 맵 정의 필요)
|
|
if ($bad_bot) {
|
|
return 403;
|
|
}
|
|
|
|
# SSL/TLS 인증서 설정
|
|
ssl_certificate /etc/letsencrypt/live/domain/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/domain/privkey.pem;
|
|
ssl_dhparam /etc/ssl/certs/domain/dhparam.pem; # openssl dhparam -out /etc/ssl/certs/domain/dhparam.pem 2048
|
|
|
|
# 최신 SSL/TLS 설정 - SSL Labs A+ 등급 달성 가능
|
|
ssl_session_cache shared:SSL:50m; # SSL 세션 캐시 크기 증가 (트래픽 많을 시 유용)
|
|
ssl_session_timeout 10m; # SSL 세션 타임아웃
|
|
ssl_session_tickets off; # 보안 향상을 위해 세션 티켓 비활성화 (nginx >= 1.5.9)
|
|
ssl_protocols TLSv1.2 TLSv1.3; # 최신 TLS 프로토콜만 사용
|
|
ssl_prefer_server_ciphers off; # TLSv1.3에서는 클라이언트 선호 암호화 사용 (모범 사례)
|
|
# 최신 암호화 스위트 - CHACHA20-POLY1305 포함 (모바일 최적화)
|
|
ssl_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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
|
|
ssl_ecdh_curve secp384r1; # ECDH 곡선 설정 (nginx >= 1.1.0)
|
|
|
|
# OCSP 스테이플링 - SSL 핸드셰이크 성능 향상
|
|
# 인증서에 OCSP URL이 없으면 자동으로 비활성화됨 (경고는 정상)
|
|
ssl_stapling on;
|
|
ssl_stapling_verify on;
|
|
ssl_trusted_certificate /etc/letsencrypt/live/domain/chain.pem;
|
|
resolver 1.1.1.1 8.8.8.8 valid=300s; # Cloudflare와 Google DNS 사용
|
|
resolver_timeout 5s;
|
|
|
|
# 보안 헤더 - 다양한 공격으로부터 보호
|
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # HSTS - 다운그레이드 공격 방지
|
|
add_header X-Frame-Options "DENY" always; # 클릭재킹 방지
|
|
add_header X-Content-Type-Options "nosniff" always; # MIME 스니핑 방지
|
|
add_header X-XSS-Protection "1; mode=block" always; # 레거시 XSS 보호
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always; # 리퍼러 정보 제어
|
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' ''; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' wss: ws:;" always;
|
|
# XSS 방지를 위한 CSP
|
|
# 'unsafe-inline'은 범용 설정이 아니며, 보안상 위험한 설정입니다. CSP의 핵심 보호 기능을 무력화시키므로, 개발 환경이나 레거시 코드 마이그레이션 과정에서만 임시로 사용하고, 프로덕션 환경에서는 nonce, hash, 또는 외부 파일 분리 방식으로 대체해야 합니다.
|
|
|
|
# 클라이언트가 업로드할 수 있는 전체 요청 본문(body) 의 최대 허용 크기
|
|
# 요청 바디(파일, 폼 데이터 등)가 이 값을 초과하면 Nginx는 즉시 413 Request Entity Too Large 에러를 반환
|
|
# 업로드 제한선.
|
|
client_max_body_size 100M; # 최대 업로드 크기 제한 (애플리케이션에 맞게 조정)
|
|
|
|
# 파일 캐시 - I/O 성능 향상
|
|
open_file_cache max=1000 inactive=20s; # 최대 1000개 파일 캐시, 20초 비활성 시 제거
|
|
open_file_cache_valid 30s; # 캐시 유효성 검사 주기
|
|
open_file_cache_min_uses 2; # 최소 2회 사용 시 캐시
|
|
open_file_cache_errors on; # 파일 오류도 캐시
|
|
|
|
# 로깅 설정 - 버퍼링으로 I/O 감소
|
|
access_log /log/nginx/domain.com.gunicorn_access.log main buffer=32k flush=5s;
|
|
error_log /log/nginx/domain.com.gunicorn_error.log warn;
|
|
|
|
# frontend에 오류 페이지 정의가 되어 있는 경우 사용 가능
|
|
# # 커스텀 오류 페이지 - 더 나은 사용자 경험 및 정보 노출 방지
|
|
# error_page 404 /404.html;
|
|
# error_page 500 502 503 504 /50x.html;
|
|
|
|
# location = /404.html {
|
|
# internal; # 내부 리다이렉트만 허용
|
|
# root /www/error_pages;
|
|
# }
|
|
|
|
# location = /50x.html {
|
|
# internal; # 내부 리다이렉트만 허용
|
|
# root /www/error_pages;
|
|
# }
|
|
|
|
# Fastapi 미디어 파일 - 사용자 업로드 파일
|
|
location /media {
|
|
autoindex off; # 디렉토리 목록 비활성화
|
|
# gzip_static on; # 사전 압축된 .gz 파일 사용
|
|
expires 30d; # 브라우저 캐시 30일 후 브라우저가 다시 요청할 때 재검증
|
|
alias /www/webroot/media; # Fastapi 프로젝트의 미디어 파일 경로
|
|
|
|
# 정적 파일 캐싱 - 브라우저 캐시 최적화
|
|
add_header Cache-Control "public, immutable";
|
|
access_log off; # 액세스 로그 비활성화로 성능 향상
|
|
}
|
|
|
|
# Fastapi 정적 파일 - CSS, JS, 이미지 등
|
|
location /static {
|
|
autoindex off; # 디렉토리 목록 비활성화
|
|
# gzip_static on; # 사전 압축된 .gz 파일 사용 압축된 파일이 없다면 설정 무의미
|
|
expires 30d; # 브라우저 캐시 30일 후 브라우저가 다시 요청할 때 재검증
|
|
alias /www/webroot/static; # Fastapi 프로젝트의 정적 파일 경로
|
|
|
|
# 정적 파일 캐싱 - 브라우저 캐시 최적화
|
|
add_header Cache-Control "public, immutable";
|
|
access_log off; # 액세스 로그 비활성화로 성능 향상
|
|
}
|
|
|
|
# 메인 애플리케이션 - 백엔드로 프록시
|
|
location / {
|
|
autoindex off; # 디렉토리 목록 비활성화
|
|
|
|
# 속도 제한 - DDoS 및 무차별 대입 공격 방지
|
|
# (nginx.conf의 http 블록에서 limit_req_zone과 limit_conn_zone 정의 필요)
|
|
limit_req zone=general burst=20 nodelay; # 초당 요청 제한
|
|
limit_conn addr 10; # IP당 동시 연결 제한
|
|
|
|
# HTTP 메서드 제한 - HTTP 동사 변조 공격 방지
|
|
limit_except GET POST HEAD OPTIONS {
|
|
deny all;
|
|
}
|
|
|
|
# 백엔드 애플리케이션으로 프록시
|
|
proxy_pass http://appname:serviceport;
|
|
|
|
# WebSocket 지원 - 실시간 통신 애플리케이션에 필수
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection $connection_upgrade; # nginx.conf에서 $connection_upgrade 맵 정의 필요
|
|
|
|
# 프록시 헤더 - 클라이언트 정보 전달
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header X-Forwarded-Host $host;
|
|
proxy_set_header X-Forwarded-Port $server_port;
|
|
|
|
# 타임아웃 설정 - 애플리케이션에 맞게 조정
|
|
proxy_connect_timeout 60s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
|
|
# 버퍼 설정 - 대화형 애플리케이션에 최적화 - Websocket 사용시 설정
|
|
# proxy_buffering off; # 즉시 응답 전달
|
|
# proxy_request_buffering off; # 즉시 요청 전달
|
|
|
|
# 버퍼링 활성화 (기본값) - 기본 fastapi 사용시 설정
|
|
proxy_buffering on;
|
|
proxy_request_buffering on;
|
|
|
|
# 버퍼 크기 설정 - 기본 fastapi 사용시 설정
|
|
proxy_buffer_size 4k;
|
|
proxy_buffers 8 4k;
|
|
proxy_busy_buffers_size 8k;
|
|
|
|
proxy_set_header Accept-Encoding gzip;
|
|
}
|
|
|
|
# Let's Encrypt 도메인 검증 프로그램 허용 (HTTPS에서도 필요시)
|
|
location ^~ /.well-known/acme-challenge/ {
|
|
allow all;
|
|
root /www/certbot;
|
|
try_files $uri =404; # 디렉토리 순회 공격 방지
|
|
}
|
|
|
|
# 정적 리소스 캐싱 - 이미지, 폰트, CSS, JS 등
|
|
# 브라우저 캐시로 로드 시간 단축 및 서버 부하 감소
|
|
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)$ {
|
|
expires 1y; # 1년 캐시
|
|
add_header Cache-Control "public, immutable";
|
|
access_log off; # 액세스 로그 비활성화로 성능 향상
|
|
}
|
|
|
|
# 닷 파일 차단 - .htaccess, .htpasswd, .svn, .git, .env 등
|
|
# 민감한 설정 파일 노출 방지
|
|
location ~ /\. {
|
|
deny all;
|
|
access_log off; # 차단된 시도 로그 비활성화
|
|
log_not_found off;
|
|
}
|
|
|
|
# 민감한 파일 확장자 차단 - 로그, 인증서, 스크립트, SQL 등
|
|
# 보안을 위해 직접 접근 차단
|
|
location ~* \.(log|binary|pem|enc|crt|conf|cnf|sql|sh|key|yml|lock)$ {
|
|
deny all;
|
|
access_log off; # 차단된 시도 로그 비활성화
|
|
log_not_found off;
|
|
}
|
|
|
|
# 민감한 설정 파일 차단 - composer, package.json, phpunit 등
|
|
# 프로젝트 메타데이터 및 설정 파일 노출 방지
|
|
location ~* (composer\.json|composer\.lock|composer\.phar|contributing\.md|license\.txt|readme\.rst|readme\.md|readme\.txt|copyright|artisan|gulpfile\.js|package\.json|phpunit\.xml|access_log|error_log|gruntfile\.js)$ {
|
|
deny all;
|
|
access_log off; # 차단된 시도 로그 비활성화
|
|
log_not_found off;
|
|
}
|
|
|
|
# 파비콘 - 로그 노이즈 제거
|
|
location = /favicon.ico {
|
|
log_not_found off;
|
|
access_log off;
|
|
}
|
|
|
|
# robots.txt - 검색 엔진 크롤러 제어
|
|
location = /robots.txt {
|
|
log_not_found off;
|
|
access_log off;
|
|
allow all;
|
|
}
|
|
}
|