From 61358291ec76f07816e3f92845bf184ad1dd4b62 Mon Sep 17 00:00:00 2001 From: pulpbot Date: Sun, 19 Jul 2026 17:47:44 +0000 Subject: [PATCH 1/3] Update CI files --- .ci/ansible/settings.py.j2 | 1 + .ci/ansible/start_container.yaml | 2 ++ pyproject.toml | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.ci/ansible/settings.py.j2 b/.ci/ansible/settings.py.j2 index ab8ebb3dce0..0b8a1a16a4d 100644 --- a/.ci/ansible/settings.py.j2 +++ b/.ci/ansible/settings.py.j2 @@ -1,3 +1,4 @@ +SECRET_KEY = "{{ django_secret }}" CONTENT_ORIGIN = "{{ pulp_scheme }}://pulp:{{ 443 if pulp_scheme == 'https' else 80 }}" ANSIBLE_API_HOSTNAME = "{{ pulp_scheme }}://pulp:{{ 443 if pulp_scheme == 'https' else 80 }}" ANSIBLE_CONTENT_HOSTNAME = "{{ pulp_scheme }}://pulp:{{ 443 if pulp_scheme == 'https' else 80 }}/pulp/content" diff --git a/.ci/ansible/start_container.yaml b/.ci/ansible/start_container.yaml index e0891b7ab5d..4ef94c861cf 100644 --- a/.ci/ansible/start_container.yaml +++ b/.ci/ansible/start_container.yaml @@ -18,6 +18,8 @@ ansible.builtin.template: src: "settings.py.j2" dest: "settings/settings.py" + vars: + django_secret: "lookup('community.general.random_string', length=50, overwrite_all='abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)')" - name: "Setup docker networking" community.docker.docker_network: diff --git a/pyproject.toml b/pyproject.toml index e44ac1a73ef..5914ca16293 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,7 +162,6 @@ replace = "version = \"{new_version}\"" [[tool.bumpversion.files]] filename = "./setup.py" - [tool.ruff] # This section is managed by the plugin template. Do not edit manually. line-length = 100 @@ -184,6 +183,7 @@ extend-select = [ [tool.ruff.lint.flake8-tidy-imports.banned-api] # This section is managed by the plugin template. Do not edit manually. "distutils".msg = "The 'distutils' module has been deprecated since Python 3.9." +"pulpcore.app.settings".msg = "Always import 'settings' from 'django.conf' instead." [tool.ruff.lint.isort] # This section is managed by the plugin template. Do not edit manually. From a4a3fc94831d4ea37e68335e74309fa02dd90e34 Mon Sep 17 00:00:00 2001 From: Matthias Dellweg Date: Tue, 14 Jul 2026 17:57:15 +0200 Subject: [PATCH 2/3] Remove insecure default Django secret --- CHANGES/+django_secret.bugfix | 1 + pulp_file/tests/functional/api/test_domains.py | 2 +- pulp_file/tests/functional/api/test_pulp_export.py | 2 +- pulpcore/app/authentication.py | 3 +-- pulpcore/app/redis_connection.py | 3 +-- pulpcore/app/serializers/exporter.py | 3 ++- pulpcore/app/serializers/importer.py | 3 ++- pulpcore/app/serializers/repository.py | 3 ++- pulpcore/app/settings.py | 3 --- pulpcore/app/views/importer.py | 2 +- pulpcore/cache/cache.py | 2 +- pulpcore/tasking/kafka.py | 2 +- pulpcore/tests/functional/api/test_auth.py | 2 +- .../tests/functional/api/using_plugin/test_filesystemexport.py | 2 +- pulpcore/tests/functional/api/using_plugin/test_pulpimport.py | 2 +- 15 files changed, 17 insertions(+), 18 deletions(-) create mode 100644 CHANGES/+django_secret.bugfix diff --git a/CHANGES/+django_secret.bugfix b/CHANGES/+django_secret.bugfix new file mode 100644 index 00000000000..571ee9046f7 --- /dev/null +++ b/CHANGES/+django_secret.bugfix @@ -0,0 +1 @@ +Stopped shipping an insecure default as DJANGO_SECRET. diff --git a/pulp_file/tests/functional/api/test_domains.py b/pulp_file/tests/functional/api/test_domains.py index 07c18500f0a..e1399763dcd 100644 --- a/pulp_file/tests/functional/api/test_domains.py +++ b/pulp_file/tests/functional/api/test_domains.py @@ -2,8 +2,8 @@ import uuid import pytest +from django.conf import settings -from pulpcore.app import settings from pulpcore.client.pulp_file import ApiException from pulpcore.client.pulpcore import ApiException as CoreApiException from pulpcore.client.pulpcore import Repair diff --git a/pulp_file/tests/functional/api/test_pulp_export.py b/pulp_file/tests/functional/api/test_pulp_export.py index 4650efc0498..d1da49f6b9f 100644 --- a/pulp_file/tests/functional/api/test_pulp_export.py +++ b/pulp_file/tests/functional/api/test_pulp_export.py @@ -1,8 +1,8 @@ import uuid import pytest +from django.conf import settings -from pulpcore.app import settings from pulpcore.client.pulpcore.exceptions import ApiException from pulpcore.constants import TASK_STATES diff --git a/pulpcore/app/authentication.py b/pulpcore/app/authentication.py index 5d5a3cdb449..26a3d8afc97 100644 --- a/pulpcore/app/authentication.py +++ b/pulpcore/app/authentication.py @@ -5,13 +5,12 @@ from gettext import gettext as _ import jq +from django.conf import settings from django.contrib.auth import authenticate from django.contrib.auth.backends import RemoteUserBackend from rest_framework.authentication import BaseAuthentication, RemoteUserAuthentication from rest_framework.exceptions import AuthenticationFailed -from pulpcore.app import settings - _logger = logging.getLogger(__name__) diff --git a/pulpcore/app/redis_connection.py b/pulpcore/app/redis_connection.py index e612680cbd0..87270c11249 100644 --- a/pulpcore/app/redis_connection.py +++ b/pulpcore/app/redis_connection.py @@ -1,8 +1,7 @@ +from django.conf import settings from redis import Redis from redis.asyncio import Redis as aRedis -from pulpcore.app.settings import settings - _conn = None _a_conn = None diff --git a/pulpcore/app/serializers/exporter.py b/pulpcore/app/serializers/exporter.py index 33339c4eda3..aec8f4adde2 100644 --- a/pulpcore/app/serializers/exporter.py +++ b/pulpcore/app/serializers/exporter.py @@ -2,10 +2,11 @@ import re from gettext import gettext as _ +from django.conf import settings from rest_framework import serializers from rest_framework.validators import UniqueValidator -from pulpcore.app import models, settings +from pulpcore.app import models from pulpcore.app.serializers import ( DetailIdentityField, DetailRelatedField, diff --git a/pulpcore/app/serializers/importer.py b/pulpcore/app/serializers/importer.py index 742ad9ab176..e940f73260a 100644 --- a/pulpcore/app/serializers/importer.py +++ b/pulpcore/app/serializers/importer.py @@ -1,11 +1,12 @@ import os from gettext import gettext as _ +from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from rest_framework import serializers from rest_framework.validators import UniqueValidator -from pulpcore.app import models, settings +from pulpcore.app import models from pulpcore.app.serializers import ( DetailIdentityField, ImportIdentityField, diff --git a/pulpcore/app/serializers/repository.py b/pulpcore/app/serializers/repository.py index d2b90ff1c4a..4aa13673322 100644 --- a/pulpcore/app/serializers/repository.py +++ b/pulpcore/app/serializers/repository.py @@ -3,10 +3,11 @@ from urllib.parse import urlparse from cryptography.x509 import load_pem_x509_certificate +from django.conf import settings from rest_framework import fields, serializers from rest_framework_nested.serializers import NestedHyperlinkedModelSerializer -from pulpcore.app import models, settings +from pulpcore.app import models from pulpcore.app.serializers import ( DetailIdentityField, DetailRelatedField, diff --git a/pulpcore/app/settings.py b/pulpcore/app/settings.py index 08a88ff8174..3663ea57dfc 100644 --- a/pulpcore/app/settings.py +++ b/pulpcore/app/settings.py @@ -82,9 +82,6 @@ # List of upload handler classes to be applied in order. FILE_UPLOAD_HANDLERS = ("pulpcore.app.files.HashingFileUploadHandler",) -# SECURITY WARNING: this should be set to a unique, unpredictable value -SECRET_KEY = "SECRET" - # Key used to encrypt fields in the database DB_ENCRYPTION_KEY = "/etc/pulp/certs/database_fields.symmetric.key" diff --git a/pulpcore/app/views/importer.py b/pulpcore/app/views/importer.py index 5e5f104588a..e49aa43ba04 100644 --- a/pulpcore/app/views/importer.py +++ b/pulpcore/app/views/importer.py @@ -2,11 +2,11 @@ import os from gettext import gettext as _ +from django.conf import settings from drf_spectacular.utils import extend_schema from rest_framework.response import Response from rest_framework.views import APIView -from pulpcore.app import settings from pulpcore.app.serializers import PulpImportCheckResponseSerializer, PulpImportCheckSerializer diff --git a/pulpcore/cache/cache.py b/pulpcore/cache/cache.py index 4fdaf7d691f..8a7ee978f51 100644 --- a/pulpcore/cache/cache.py +++ b/pulpcore/cache/cache.py @@ -5,6 +5,7 @@ from aiohttp.web import FileResponse, HTTPSuccessful, Request, Response from aiohttp.web_exceptions import HTTPFound +from django.conf import settings from django.http import FileResponse as ApiFileResponse from django.http import HttpResponse, HttpResponseRedirect from redis import ConnectionError @@ -16,7 +17,6 @@ get_async_redis_connection, get_redis_connection, ) -from pulpcore.app.settings import settings from pulpcore.responses import ArtifactResponse DEFAULT_EXPIRES_TTL = settings.CACHE_SETTINGS["EXPIRES_TTL"] diff --git a/pulpcore/tasking/kafka.py b/pulpcore/tasking/kafka.py index 3f6f0fd447d..808e20d7bbf 100644 --- a/pulpcore/tasking/kafka.py +++ b/pulpcore/tasking/kafka.py @@ -6,7 +6,7 @@ from django.conf import settings -_bootstrap_servers = settings.get("KAFKA_BOOTSTRAP_SERVERS") +_bootstrap_servers = getattr(settings, "KAFKA_BOOTSTRAP_SERVERS") if _bootstrap_servers is None: diff --git a/pulpcore/tests/functional/api/test_auth.py b/pulpcore/tests/functional/api/test_auth.py index effd89c604d..b8b50111529 100644 --- a/pulpcore/tests/functional/api/test_auth.py +++ b/pulpcore/tests/functional/api/test_auth.py @@ -8,8 +8,8 @@ from base64 import b64encode import pytest +from django.conf import settings -from pulpcore.app import settings from pulpcore.client.pulpcore import ApiException diff --git a/pulpcore/tests/functional/api/using_plugin/test_filesystemexport.py b/pulpcore/tests/functional/api/using_plugin/test_filesystemexport.py index 414b1b94513..bd2e4f516da 100644 --- a/pulpcore/tests/functional/api/using_plugin/test_filesystemexport.py +++ b/pulpcore/tests/functional/api/using_plugin/test_filesystemexport.py @@ -8,8 +8,8 @@ import uuid import pytest +from django.conf import settings -from pulpcore.app import settings from pulpcore.client.pulp_file import RepositorySyncURL from pulpcore.client.pulpcore.exceptions import ApiException diff --git a/pulpcore/tests/functional/api/using_plugin/test_pulpimport.py b/pulpcore/tests/functional/api/using_plugin/test_pulpimport.py index ef34be5a060..d3067da5069 100644 --- a/pulpcore/tests/functional/api/using_plugin/test_pulpimport.py +++ b/pulpcore/tests/functional/api/using_plugin/test_pulpimport.py @@ -11,8 +11,8 @@ from pathlib import Path import pytest +from django.conf import settings -from pulpcore.app import settings from pulpcore.client.pulp_file import RepositorySyncURL from pulpcore.client.pulpcore.exceptions import ApiException From 643df5f6a4bec3b5caf4775b15d5f83bfb68f763 Mon Sep 17 00:00:00 2001 From: Pedro Brochado Date: Tue, 21 Jul 2026 17:42:26 -0300 Subject: [PATCH 3/3] Fix dynaconf's django integration code Thre should be no settings activity after dyanconf's instantiation. Assisted-by: Claude (Claude Code) (cherry picked from commit 0a06d305de683b9236bccaa92c56af7369b1af06) --- pulpcore/app/settings.py | 90 +++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/pulpcore/app/settings.py b/pulpcore/app/settings.py index 3663ea57dfc..db95dfe76bb 100644 --- a/pulpcore/app/settings.py +++ b/pulpcore/app/settings.py @@ -11,13 +11,13 @@ import sys from contextlib import suppress from importlib import import_module -from logging import getLogger from pathlib import Path from cryptography.fernet import Fernet from django.conf import global_settings from django.core.exceptions import ImproperlyConfigured -from django.core.files.storage import storages # noqa: F401 +from django.core.files.storage import storages +from dynaconf import DjangoDynaconf, Validator from pulpcore import constants @@ -372,7 +372,6 @@ # HERE STARTS DYNACONF EXTENSION LOAD (Keep at the very bottom of settings.py) # Read more at https://www.dynaconf.com/django/ -from dynaconf import DjangoDynaconf, Validator # noqa # Validators storage_keys = ("STORAGES.default.BACKEND", "DEFAULT_FILE_STORAGE") @@ -463,6 +462,50 @@ messages={"is_type_of": "{name} must be a dictionary."}, ) + +def api_root_hook(settings): + # protocol://host:port/{API_ROOT}{domain}/api/{version}/ + # All of the below are DEPRECATED, and should be replaced by calling + # pulpcore.plugin.find_url.find_api_root() (q.v.) + if settings.API_ROOT_REWRITE_HEADER: + api_root = "//" + else: + api_root = settings.API_ROOT + return { + "V3_API_ROOT": api_root + "api/v3/", + "V3_DOMAIN_API_ROOT": api_root + "/api/v3/", + "V3_API_ROOT_NO_FRONT_SLASH": (api_root + "api/v3/").lstrip("/"), + "V3_DOMAIN_API_ROOT_NO_FRONT_SLASH": (api_root + "/api/v3/").lstrip("/"), + } + + +def forbidden_checksums_hook(settings): + return { + "FORBIDDEN_CHECKSUMS": sorted( + set(constants.ALL_KNOWN_CONTENT_CHECKSUMS).difference( + settings.ALLOWED_CONTENT_CHECKSUMS + ) + ), + } + + +def validate_db_encryption_key_hook(settings): + if Path(sys.argv[0]).name in ["pytest", "sphinx-build"] or ( + len(sys.argv) >= 2 and sys.argv[1] in ["collectstatic", "openapi"] + ): + return {} + try: + with open(settings.DB_ENCRYPTION_KEY, "rb") as key_file: + Fernet(key_file.read()) + except Exception as ex: + raise ImproperlyConfigured( + "Could not load DB_ENCRYPTION_KEY file '{file}': {err}".format( + file=settings.DB_ENCRYPTION_KEY, err=ex + ) + ) + return {} + + settings = DjangoDynaconf( __name__, ENVVAR_PREFIX_FOR_DYNACONF="PULP", @@ -481,40 +524,19 @@ json_header_auth_validator, authentication_json_header_openapi_security_scheme_validator, ], + post_hooks=( + api_root_hook, + forbidden_checksums_hook, + validate_db_encryption_key_hook, + ), ) -# HERE ENDS DYNACONF EXTENSION LOAD (No more code below this line) -_logger = getLogger(__name__) - - -if not ( - Path(sys.argv[0]).name in ["pytest", "sphinx-build"] - or (len(sys.argv) >= 2 and sys.argv[1] in ["collectstatic", "openapi"]) -): - try: - with open(DB_ENCRYPTION_KEY, "rb") as key_file: - Fernet(key_file.read()) - except Exception as ex: - raise ImproperlyConfigured( - ("Could not load DB_ENCRYPTION_KEY file '{file}': {err}").format( - file=DB_ENCRYPTION_KEY, err=ex - ) - ) - - -FORBIDDEN_CHECKSUMS = set(constants.ALL_KNOWN_CONTENT_CHECKSUMS).difference( - ALLOWED_CONTENT_CHECKSUMS -) +# begin compatibility layer for DEFAULT_FILE_STORAGE +# Remove on pulpcore=3.85 or pulpcore=4.0 -# Ensures the cached property storage.backends uses the right value after dynaconf init +# Ensures the cached property storage.backends uses the the right value storages._backends = settings.STORAGES.copy() storages.backends +# end compatibility layer -if settings.API_ROOT_REWRITE_HEADER: - api_root = "//" -else: - api_root = settings.API_ROOT -settings.set("V3_API_ROOT", api_root + "api/v3/") # Not user configurable -settings.set("V3_DOMAIN_API_ROOT", api_root + "/api/v3/") -settings.set("V3_API_ROOT_NO_FRONT_SLASH", settings.V3_API_ROOT.lstrip("/")) -settings.set("V3_DOMAIN_API_ROOT_NO_FRONT_SLASH", settings.V3_DOMAIN_API_ROOT.lstrip("/")) +# HERE ENDS DYNACONF EXTENSION LOAD (No more code below this line)