Files
ACORE/acore/settings/production.py
T
lokses 719d0b737b infra: надёжное обнаружение тестов, Redis-кэш и хардненинг прода
- AcoreDiscoverRunner собирает tests/tests_* всех локальных приложений
  (manage.py test находил 38 тестов из ~466 — теперь все).
- CACHES на Redis (отдельная DB от Celery) + обязательный requirepass
  в production, SECURE_HSTS_PRELOAD.
- Фикс stored-XSS в админке: format_html вместо mark_safe + f-string.
- Утилиты безопасности/тестов, шифрование полей.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 18:03:11 +03:00

69 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from urllib.parse import urlparse
from django.core.exceptions import ImproperlyConfigured
from .base import * # noqa
DEBUG = False
# Traefik / reverse proxy terminates TLS and forwards HTTP with X-Forwarded-Proto.
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
USE_X_FORWARDED_HOST = env.bool('USE_X_FORWARDED_HOST', default=True)
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = True
CSRF_TRUSTED_ORIGINS = env.list('CSRF_TRUSTED_ORIGINS', default=[])
# Общий кэш между gunicorn-воркерами (троттлинг логина). Отдельная Redis DB
# от Celery broker (/0). Задайте CACHE_URL с requirepass в .env.
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': env('CACHE_URL', default='redis://127.0.0.1:6379/1'),
}
}
def _redis_url_has_password(url: str) -> bool:
if not url or not str(url).startswith(('redis://', 'rediss://')):
return True # non-redis backends are out of scope here
return bool(urlparse(str(url)).password)
def _assert_redis_auth(name: str, url: str) -> None:
if _redis_url_has_password(url):
return
# Temporary escape for cutover only — remove after requirepass is set.
if env.bool('REDIS_ALLOW_UNAUTHENTICATED', default=False):
import logging
logging.getLogger(__name__).warning(
'%s has no Redis password; set requirepass and remove '
'REDIS_ALLOW_UNAUTHENTICATED',
name,
)
return
raise ImproperlyConfigured(
f'{name} must include a Redis password in production '
f'(redis://:PASSWORD@127.0.0.1:6379/N). '
f'Set requirepass on redis-server, or temporarily '
f'REDIS_ALLOW_UNAUTHENTICATED=1 during cutover.'
)
# В production ключ шифрования полей обязан быть задан явно (не fallback на SECRET_KEY).
if not FIELD_ENCRYPTION_KEY:
raise ImproperlyConfigured(
'FIELD_ENCRYPTION_KEY must be set in production (.env). '
'Use a value distinct from SECRET_KEY when rotating secrets.'
)
# Redis без пароля + reachable broker = enqueue Celery (в т.ч. bulk identity).
_assert_redis_auth('CACHE_URL', CACHES['default']['LOCATION'])
_assert_redis_auth('CELERY_BROKER_URL', CELERY_BROKER_URL)
if CELERY_RESULT_BACKEND:
_assert_redis_auth('CELERY_RESULT_BACKEND', CELERY_RESULT_BACKEND)