o2o-infrakit/config/web-server/sample_nginx_https.conf

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;
}
}