iac/apps/django/aero/django-configmap.yaml
emelinda b5471e664d Django: убран домен чужого контура из настроек и адресов backend
Браузер получал 404 на
  https://sarex.contour.infra.sarex.tech/media/state/previews/....png
то есть backend отдавал АБСОЛЮТНЫЕ ссылки на чужой хост. Файлы при этом
лежали у нас — уходил не тот домен.

Источников оказалось три, и все в base:

  * django-configmap, HOST = "https://sarex.contour.infra.sarex.tech" —
    именно по нему строятся абсолютные ссылки на медиа;
  * там же CSRF_TRUSTED_ORIGINS с тем же доменом: без нашего домена django
    отклонял бы POST-формы админки по проверке Referer;
  * envs backend SERVER_API_HOST и SERVER_HOST. У celery те же две
    переменные я поправил раньше, а у backend пропустил.

Конфигмап скопирован в оверлей целиком: все настройки лежат одним значением
data.production.py на 331 строку, kustomize заменяет его только целиком —
та же причина, что у nginx-configmap рядом. Изменены ровно две строки,
остальное побайтно совпадает с base, так что diff остаётся читаемым.

После правки домена sarex.contour.infra.sarex.tech в рендере
clusters/aero/apps не остаётся ни одного вхождения.
2026-08-10 08:13:07 +03:00

349 lines
13 KiB
YAML
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.

# Копия django-configmap из base с заменой домена на домен контура.
#
# Патчем этого не сделать: все настройки лежат ОДНИМ значением data.production.py
# (331 строка python), kustomize умеет заменить его только целиком. Ровно та же
# причина, что у nginx-configmap.yaml рядом.
#
# ЧТО ИЗМЕНЕНО — две строки, обе про домен sarex.contour.infra.sarex.tech,
# принадлежащий ДРУГОМУ контуру:
#
# HOST — по нему backend строит АБСОЛЮТНЫЕ ссылки на медиа.
# Браузер получал
# https://sarex.contour.infra.sarex.tech/media/state/previews/....png
# и, разумеется, 404: чужой хост, наших файлов там нет.
# CSRF_TRUSTED_ORIGINS — без нашего домена django отклонял бы POST-формы
# админки с ошибкой проверки Referer.
#
# Больше в файле ничего не менялось: сравнивать с base удобно обычным diff.
apiVersion: v1
kind: ConfigMap
metadata:
name: django-configmap
namespace: django
data:
production.py: |
import ast
import os
from .base import *
from logging.handlers import SysLogHandler
from datetime import timedelta
def _load_env_file(path):
try:
with open(path, "r", encoding="utf-8") as f:
for raw_line in f:
line = raw_line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip()
if len(value) >= 2 and value[0] == value[-1] and value[0] in ("'", '"'):
try:
value = ast.literal_eval(value)
except (ValueError, SyntaxError):
value = value[1:-1]
if key and key not in os.environ:
os.environ[key] = value
except FileNotFoundError:
pass
def _read_secret_file(path, default=""):
try:
with open(path, "r", encoding="utf-8") as f:
return f.read().strip()
except FileNotFoundError:
return default
# Fallback for manage.py launched via `kubectl exec` (outside entrypoint),
# so Django can still read DB/JWT values from Vault-injected files.
_load_env_file("/vault/secrets/django-postgresql")
_load_env_file("/vault/secrets/django-rabbitmq")
_load_env_file("/vault/secrets/django-s3")
_load_env_file("/vault/secrets/django-kafka")
_load_env_file("/vault/secrets/django-common")
if not os.environ.get("JWT_PRIVATE_KEY"):
os.environ["JWT_PRIVATE_KEY"] = _read_secret_file("/vault/secrets/django-jwt-private")
if not os.environ.get("JWT_PUBLIC_KEY"):
os.environ["JWT_PUBLIC_KEY"] = _read_secret_file("/vault/secrets/django-jwt-public")
ALLOWED_HOSTS = ["*"]
FILE_UPLOAD_PERMISSIONS = 0o644
DEBUG = False
CSRF_COOKIE_SECURE = True
CSRF_TRUSTED_ORIGINS = ["https://sarex.local.lonsdaleites.ru", "http://sarex.local.lonsdaleites.ru"]
SESSION_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = False
SECRET_KEY = 't2=9+($2f%7ptsdy4!rby$)mcfl1l%o2e@vs^d(g&(wwi&%k1v'
CORS_ORIGIN_ALLOW_ALL = True
SERVERSETTINGS.cache_enabled = True
INSTALLED_APPS = list(INSTALLED_APPS) + ['corsheaders']
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
)
BASIC_USER_ID = 2
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'user-agent',
'x-csrftoken',
'x-requested-with',
'x-token',
'Bearer',
)
HOST = "https://sarex.local.lonsdaleites.ru"
POSTGRES_DATABASE = os.environ.get('DJANGO_POSTGRES_DATABASE')
POSTGRES_USER = os.environ.get('DJANGO_POSTGRES_USER')
POSTGRES_PASSWORD = os.environ.get('DJANGO_POSTGRES_PASSWORD')
POSTGRES_HOST = os.environ.get('DJANGO_POSTGRES_HOST')
POSTGRES_PORTS = os.environ.get('DJANGO_POSTGRES_PORTS', "5432")
DATABASES = {
'default': {
'ENGINE': 'django_prometheus.db.backends.postgresql',
'NAME': POSTGRES_DATABASE,
'USER': POSTGRES_USER,
'PASSWORD': POSTGRES_PASSWORD,
'HOST': POSTGRES_HOST,
'PORT': POSTGRES_PORTS,
}
}
MEASUREMENTSETTINGS = MeasurementSettings()
WORKFLOWSSETTINGS = WorkFlowsSettings()
S3SETTINGS = S3Settings()
GATEWAYSETTINGS = GateWaySetttings()
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
}
},
'formatters': {
'verbose': {
'format': '[contactor] %(levelname)s %(asctime)s %(message)s',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
'sentry': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'logging.StreamHandler',
},
},
'loggers': {
'': {
'handlers': ['console', 'sentry'],
'level': 'INFO',
'propagate': False,
},
}
}
COMPARATOR_JWT = os.environ.get("COMPARATOR_JWT", "default_jwt")
COMPARATOR_URL = os.environ.get("COMPARATOR_URL", "https://wb.sarex.io/comparator")
COMPARATOR_SECTION = os.environ.get("COMPARATOR_SECTION", "sarex-production-storage")
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'RS512',
'SIGNING_KEY': os.environ.get("JWT_PRIVATE_KEY", "").replace("\\n", "\n"),
'VERIFYING_KEY': os.environ.get("JWT_PUBLIC_KEY", "").replace("\\n", "\n"),
'AUDIENCE': None,
'ISSUER': os.environ.get('SIMPLE_JWT_ISSUER', 'default_issuer'),
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
DEFAULT_FILE_STORAGE = 'sarex.core.storages.CustomS3Boto3Storage'
DATA_UPLOAD_MAX_MEMORY_SIZE = 268435456
if not os.environ.get('ISOLATED', False):
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn="https://3df2f4b8d3d14595a06c92e9d7c562cb@sentry.io/1501541",
integrations=[DjangoIntegration()],
environment=os.environ.get('SENTRY_ENVIRONMENT', 'production'),
send_default_pii=True,
)
COMPARISON_API_URL = f"{os.environ.get('WORKFLOWSSETTINGS_HOST')}/comparisons"
DOCUMENTATION_API_URL = f"{os.environ.get('WORKFLOWSSETTINGS_HOST')}/documentations"
PDM_FILES_API_URL = f"{os.environ.get('WORKFLOWSSETTINGS_HOST')}/files"
WORKFLOWS_TASKS = {
"update_orthomosaic_data": {
"image": f"{os.environ.get('WORKFLOWSSETTINGS_REGISTRY')}/update-orthomosaic-data:dev",
"service_requests": ["django-auth"],
"backoff_limit": 3,
},
}
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': (
'rest_framework.pagination.LimitOffsetPagination' ),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
'PAGE_SIZE': 1000, 'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend' ],
'DEFAULT_AUTHENTICATION_CLASSES': [
'sarex.authentication.backends.ZitadelJWTAuthentication',
'rest_framework.authentication.RemoteUserAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'sarex.authentication.backends.JWTAuthentication' ],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated', ] }
AUTHENTICATION_BACKENDS = [
'sarex.authentication.backends.CustomRemoteUserBackend',
'django.contrib.auth.backends.ModelBackend',
'guardian.backends.ObjectPermissionBackend',
]
MIDDLEWARE = [
'django_prometheus.middleware.PrometheusBeforeMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
#'django_keycloak.middlewares.AuthorizationHeaderMiddleware',
#'django_keycloak.middlewares.KeycloakSessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
#'django.contrib.auth.middleware.RemoteUserMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_user_agents.middleware.UserAgentMiddleware',
'simple_history.middleware.HistoryRequestMiddleware',
'django_prometheus.middleware.PrometheusAfterMiddleware', ]
class KeyCloakSettings(BaseSettings):
client_id: str = "client_id"
client_secret: str = "client_secret"
discovery_url: str = "https://login.wb.sarex.io/realms/sarex/.well-known/openid-configuration"
staff: Optional[str] = "Sarex staff"
superuser: Optional[str] = "Sarex superusers"
sync_with_django: bool = True
sync_admin: bool = False
group_prefix: str = 'Sarex-Role'
company_prefix: str = 'Sarex-Company'
department_prefix: str = 'Sarex-Department'
position_prefix: str = 'Sarex-Position'
separator: str = '__'
sync_user_groups: bool = False
sync_user_positions: bool = False
sync_user_departments: bool = False
sync_user_companies: bool = False
use_redirect_logout: bool = False
logout_redirect_uri: str = "/"
default_group_name: Optional[str] = 'Тест'
default_company_name: Optional[str] = 'Брусника'
trusted_uri: List[str] = ['/api/core/orthophotos/', '/api/token', '/api/token/me']
trusted_uri: List[str] = []
class Config:
env_prefix = "KC_"
KEYCLOAKSETTINGS = KeyCloakSettings()
REMOTE_USER_DEFAULT_COMPANY_ID = 1
SAREX_MODULES = [
{
"name": "Замечания",
"uri": "/remarks"
},
# {
# "name": "Управление проектами",
# "uri": "/management/projects",
# },
{
"name": "Замечания V2",
"uri": "/issues"
},
{
"name": "Документация",
"uri": "/documentations",
},
{
"name": "Согласование документов",
"uri": "/reviews"
},
{
"name": "Рабочие процессы",
"uri": "/processes"
},
{
"name": "Запросы",
"uri": "/rfi"
},
{
"name": "Управление проектами",
"uri": "/management/projects"
},
# {
# "name": "Обзор",
# "uri": "/projects"
# },
{
"name": "Передача документации",
"uri": "/transmittal"
},
]
AUTH_SETTINGS = {
"refresh_token": False,
"refresh_token_uri": "/api/token/me",
"refresh_oauth_token": True,
"refresh_oauth_token_uri": "/oauth/token",
"refresh_time": 240,
}
DEBUG=True
WEB_APP_AUTH_MODE='jwt-session-based'
SAREX_MODULES_SETTINGS = {
"aero": {
"enable_new_media": True
},
"sso_logout_redirect": True
}