convert to gitea
This commit is contained in:
298
apps/web/config/settings.py
Normal file
298
apps/web/config/settings.py
Normal file
@ -0,0 +1,298 @@
|
||||
"""
|
||||
Django settings for config project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.2.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.2/ref/settings/
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import logging.handlers # 로테이팅 파일 핸들러 임포트
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent # 프로젝트 설정 폴더(config)의 부모, 즉 /data/gyber/apps/web/
|
||||
|
||||
# ==============================================================================
|
||||
# 핵심 보안 설정 (프로덕션 환경)
|
||||
# ==============================================================================
|
||||
|
||||
# SECRET_KEY: 환경 변수 'DJANGO_SECRET_KEY' 에서 로드. 프로덕션에서는 반드시 설정해야 함.
|
||||
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
|
||||
if not SECRET_KEY:
|
||||
# 개발 환경에서는 임시 키를 사용할 수 있지만, 프로덕션에서는 에러 발생시킴
|
||||
if os.environ.get('DJANGO_DEVELOPMENT_MODE') == 'True': # 개발 모드 식별용 환경 변수 (선택적)
|
||||
SECRET_KEY = 'django-insecure-temporary-dev-key-for-gyber'
|
||||
print("WARNING: Using a temporary SECRET_KEY for development. Set DJANGO_SECRET_KEY in production.")
|
||||
else:
|
||||
raise ValueError("프로덕션 환경에서는 DJANGO_SECRET_KEY 환경 변수를 반드시 설정해야 합니다.")
|
||||
|
||||
# DEBUG 모드: 프로덕션이므로 False 로 고정 (필수!)
|
||||
DEBUG = False
|
||||
|
||||
# 허용 호스트: 외부 접속 도메인 및 필요한 내부 IP 명시
|
||||
ALLOWED_HOSTS = [
|
||||
'gyber.oneunivrs.com', # 실제 서비스 도메인
|
||||
'192.168.100.10', # 내부 테스트/접근용 IP (필요시)
|
||||
# 'localhost', '127.0.0.1' # 로컬 테스트용 (프로덕션에서는 제거 고려)
|
||||
]
|
||||
# 환경 변수에서 추가 호스트 로드 (선택적)
|
||||
# additional_hosts = os.environ.get('DJANGO_ADDITIONAL_ALLOWED_HOSTS')
|
||||
# if additional_hosts:
|
||||
# ALLOWED_HOSTS.extend([h.strip() for h in additional_hosts.split(',')])
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# 애플리케이션 정의
|
||||
# ==============================================================================
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'gyber', # Gyber 앱
|
||||
# Third-party apps
|
||||
'widget_tweaks',
|
||||
'mozilla_django_oidc',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'mozilla_django_oidc.middleware.SessionRefresh',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'config.urls'
|
||||
|
||||
# ==============================================================================
|
||||
# 템플릿 설정
|
||||
# ==============================================================================
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR, 'templates')], # BASE_DIR 은 /data/gyber/apps/web/
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
# 'django.template.context_processors.debug', # DEBUG=False 이므로 주석 처리 또는 유지해도 무방
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'gyber.context_processors.theme_processor', # ★★★ 커스텀 테마 컨텍스트 프로세서 추가 ★★★
|
||||
'gyber.context_processors.auth_context',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'config.wsgi.application' # /data/gyber/apps/web/config/wsgi.py
|
||||
|
||||
# ==============================================================================
|
||||
# 데이터베이스 설정
|
||||
# ==============================================================================
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': os.environ.get('DB_NAME', 'gyber'),
|
||||
'USER': os.environ.get('DB_USER', 'gyber'),
|
||||
'PASSWORD': os.environ.get('DB_PASSWORD'), # 환경 변수에서 로드 (필수!)
|
||||
'HOST': os.environ.get('DB_HOST', '127.0.0.1'), # DB 서버 IP 또는 호스트명
|
||||
'PORT': os.environ.get('DB_PORT', '3306'),
|
||||
'OPTIONS': {
|
||||
'charset': 'utf8mb4',
|
||||
},
|
||||
}
|
||||
}
|
||||
if not DATABASES['default']['PASSWORD']:
|
||||
raise ValueError("DB_PASSWORD 환경 변수가 설정되지 않았습니다.")
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# 비밀번호 검증
|
||||
# ==============================================================================
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},
|
||||
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},
|
||||
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},
|
||||
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',},
|
||||
]
|
||||
|
||||
# ==============================================================================
|
||||
# 국제화 및 현지화
|
||||
# ==============================================================================
|
||||
|
||||
LANGUAGE_CODE = 'ko-kr' # 한국어
|
||||
TIME_ZONE = 'Asia/Seoul' # 한국 시간
|
||||
USE_I18N = True
|
||||
USE_TZ = True # 시간대 인식 날짜/시간 사용
|
||||
|
||||
# ==============================================================================
|
||||
# 정적 파일 (Static files) 설정
|
||||
# ==============================================================================
|
||||
|
||||
STATIC_URL = 'static/' # 템플릿에서 정적 파일 접근 시 URL 프리픽스
|
||||
# 'collectstatic' 명령으로 모든 정적 파일을 모을 디렉토리 (Nginx 설정과 일치)
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # /data/gyber/apps/web/staticfiles/
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'static'), # BASE_DIR 은 /data/gyber/apps/web/
|
||||
]
|
||||
|
||||
# ==============================================================================
|
||||
# 기본 Primary Key 필드 타입
|
||||
# ==============================================================================
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
# ==============================================================================
|
||||
# 인증 및 권한 (OIDC - 프로덕션)
|
||||
# ==============================================================================
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'gyber.oidc.CustomOIDCAuthenticationBackend',
|
||||
)
|
||||
|
||||
# --- OIDC 설정 (환경 변수에서 민감 정보 로드) ---
|
||||
OIDC_RP_CLIENT_ID = os.environ.get("OIDC_RP_CLIENT_ID", "8a61fd65-70ec-4d02-8792-cc516b0746bc") # 기본값 제공 가능 (개발용)
|
||||
OIDC_RP_CLIENT_SECRET = os.environ.get("OIDC_RP_CLIENT_SECRET") # 환경 변수에서 로드 (필수!)
|
||||
if not OIDC_RP_CLIENT_SECRET:
|
||||
raise ValueError("OIDC_RP_CLIENT_SECRET 환경 변수가 설정되지 않았습니다.")
|
||||
|
||||
OIDC_RP_SIGN_ALGO = "RS256"
|
||||
OIDC_RP_SCOPES = "profile email openid"
|
||||
|
||||
AZURE_TENANT_ID = os.environ.get("AZURE_TENANT_ID", "1e8605cc-8007-46b0-993f-b388917f9499") # 기본값 제공 가능
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT = f"https://login.microsoftonline.com/{AZURE_TENANT_ID}/oauth2/v2.0/authorize"
|
||||
OIDC_OP_TOKEN_ENDPOINT = f"https://login.microsoftonline.com/{AZURE_TENANT_ID}/oauth2/v2.0/token"
|
||||
OIDC_OP_USER_ENDPOINT = "https://graph.microsoft.com/oidc/userinfo"
|
||||
OIDC_OP_JWKS_ENDPOINT = f"https://login.microsoftonline.com/{AZURE_TENANT_ID}/discovery/v2.0/keys"
|
||||
OIDC_OP_LOGOUT_ENDPOINT = f"https://login.microsoftonline.com/{AZURE_TENANT_ID}/oauth2/v2.0/logout"
|
||||
|
||||
OIDC_CREATE_USER_CALLBACK = "gyber.oidc.map_azure_user"
|
||||
OIDC_UPDATE_USER_CALLBACK = "gyber.oidc.map_azure_user"
|
||||
|
||||
# 프로덕션 로그아웃 후 리디렉션 URI (HTTPS, 지정된 포트 포함)
|
||||
OIDC_RP_POST_LOGOUT_REDIRECT_URI = f"https://gyber.oneunivrs.com:8438/oidc/authenticate/"
|
||||
|
||||
# 프로덕션 환경 OIDC 보안 설정 (HTTPS 사용 가정)
|
||||
OIDC_VERIFY_SSL = True # 외부 CA 인증서 사용 시 True
|
||||
OIDC_REDIRECT_STATE_SECURE = True # HTTPS 사용 시 True
|
||||
|
||||
OIDC_SESSION_MANAGEMENT_ENABLE = True
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||
|
||||
# --- 로그인/로그아웃 URL 설정 ---
|
||||
LOGIN_URL = "/oidc/authenticate/"
|
||||
LOGIN_REDIRECT_URL = "/dashboard/"
|
||||
LOGOUT_REDIRECT_URL = LOGIN_URL # 로그아웃 후 다시 로그인 페이지로
|
||||
|
||||
# ==============================================================================
|
||||
# 로깅 설정 (프로덕션 - 파일 로깅 중심)
|
||||
# ==============================================================================
|
||||
|
||||
# 로그 파일을 저장할 디렉토리 경로 (/data/gyber/apps/web/logs)
|
||||
LOGS_DIR = os.path.join(BASE_DIR, 'logs') # 앱별로 구분
|
||||
# 로그 디렉토리가 없으면 생성 (Gunicorn 실행 사용자에게 쓰기 권한 필요)
|
||||
os.makedirs(LOGS_DIR, exist_ok=True)
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '{levelname} {asctime} {module} [{process:d}:{thread:d}] {message}',
|
||||
'style': '{',
|
||||
},
|
||||
'simple': {
|
||||
'format': '[{levelname}] {asctime} {module}: {message}', # 시간과 모듈 정보 추가
|
||||
'style': '{',
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console': { # 개발 및 systemd journal 확인용
|
||||
'level': 'INFO',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple',
|
||||
},
|
||||
'file_django': { # 일반 Django 및 앱 로그
|
||||
'level': 'INFO', # 프로덕션에서는 INFO 부터 기록
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': os.path.join(LOGS_DIR, 'django_app.log'),
|
||||
'maxBytes': 1024 * 1024 * 20, # 20MB
|
||||
'backupCount': 5,
|
||||
'formatter': 'verbose',
|
||||
'encoding': 'utf-8',
|
||||
},
|
||||
'file_oidc': { # OIDC 관련 로그
|
||||
'level': 'DEBUG', # OIDC는 문제 발생 시 상세 로그 필요
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': os.path.join(LOGS_DIR, 'oidc.log'),
|
||||
'maxBytes': 1024 * 1024 * 10,
|
||||
'backupCount': 3,
|
||||
'formatter': 'verbose',
|
||||
'encoding': 'utf-8',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'django': {
|
||||
'handlers': ['console', 'file_django'],
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
'django.request': { # 4xx, 5xx 에러 등
|
||||
'handlers': ['file_django'], # 에러는 파일에 상세히
|
||||
'level': 'WARNING',
|
||||
'propagate': False,
|
||||
},
|
||||
'gyber': { # 'gyber' 앱의 로그
|
||||
'handlers': ['console', 'file_django'],
|
||||
'level': os.environ.get('DJANGO_GYBER_LOG_LEVEL', 'INFO'), # 환경 변수로 앱 로그 레벨 제어
|
||||
'propagate': False,
|
||||
},
|
||||
'mozilla_django_oidc': {
|
||||
'handlers': ['console', 'file_oidc'],
|
||||
'level': os.environ.get('DJANGO_OIDC_LOG_LEVEL', 'INFO'), # OIDC 로그 레벨도 제어
|
||||
'propagate': False,
|
||||
},
|
||||
},
|
||||
# 루트 로거: 특별히 설정하지 않은 다른 모든 로거의 로그를 처리 (필요시 활성화)
|
||||
# 'root': {
|
||||
# 'handlers': ['console'],
|
||||
# 'level': 'WARNING',
|
||||
# },
|
||||
}
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# 보안 강화 설정 (HTTPS 필수)
|
||||
# ==============================================================================
|
||||
# Nginx에서 SSL/TLS를 처리하고 X-Forwarded-Proto 헤더를 올바르게 전달한다고 가정
|
||||
|
||||
# Django가 프록시 뒤에서 HTTPS를 인지하도록 설정 (Nginx 설정과 일치해야 함)
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
|
||||
# HTTPS를 통해서만 쿠키 전송
|
||||
CSRF_COOKIE_SECURE = True
|
||||
SESSION_COOKIE_SECURE = True
|
||||
|
||||
# 브라우저가 항상 HTTPS로만 접속하도록 강제 (HSTS)
|
||||
SECURE_HSTS_SECONDS = 31536000 # 1년
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||
SECURE_HSTS_PRELOAD = True # HSTS 프리로드 리스트에 등록하려면 True (신중히 결정)
|
||||
|
||||
# 클릭재킹 방지
|
||||
X_FRAME_OPTIONS = 'SAMEORIGIN' # 'DENY' 또는 'SAMEORIGIN'
|
||||
Reference in New Issue
Block a user