pulp-container 2.22.6__tar.gz → 2.22.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pulp_container-2.22.6 → pulp_container-2.22.7}/CHANGES.md +9 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/PKG-INFO +2 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/__init__.py +1 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/models.py +0 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/registry_api.py +4 -2
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/serializers.py +8 -17
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/sign.py +4 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/sync_stages.py +4 -2
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/synchronize.py +0 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/urls.py +0 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/utils.py +55 -26
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/viewsets.py +0 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/constants.py +0 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_build_images.py +4 -8
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_sync_signatures.py +67 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container.egg-info/PKG-INFO +2 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container.egg-info/requires.txt +1 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pyproject.toml +1 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/requirements.txt +2 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/setup.py +1 -1
- {pulp_container-2.22.6 → pulp_container-2.22.7}/COMMITMENT +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/COPYRIGHT +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/LICENSE +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/MANIFEST.in +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/README.rst +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/functest_requirements.txt +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/access_policy.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/authorization.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/cache.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/checks.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/content.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/downloaders.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/exceptions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/fields.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/global_access_conditions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/json_schemas.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/management/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/management/commands/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/management/commands/container-handle-image-data.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/management/commands/container-repair-media-type.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0001_initial.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0002_containerrepository.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0003_oci_mediatype.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0004_upload.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0005_contentredirectcontentguard.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0006_containerpushrepository.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0007_clear_tags_artifacts_refs.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0008_include_exclude_tags.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0009_container_namespace.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0010_remove_uploadchunk.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0011_add_container_repository_permissions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0012_add_container_namespace_permissions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0013_add_pull_push_permissions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0014_containerdistribution_private.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0015_manage_tags_push_repo.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0016_add_delete_versions_permission.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0017_add_granular_perms.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0018_containerdistribution_description.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0019_DATA_distribution_model_swap.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0020_update_push_repo_perms.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0021_data_move_redirect_content_guard_to_core.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0022_delete_contentredirectcontentguard.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0023_manifestsignature.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0024_containerremote_sigstore.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0025_signature_stored_in_textfield.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0026_manifest_signing_service.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0027_data_translate_perms_to_roles.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0028_add_role_manage_permissions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0029_remove_blob_media_type.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0030_enforce_tagged_manifest_reference.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0031_replace_charf_with_textf.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0032_upload_artifact.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0033_raise_warning_for_repair.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0034_translate_signed_schema.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0035_alter_blob_content_ptr_and_more.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0036_containerpushrepository_pending_blobs_manifests.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0037_create_pull_through_cache_models.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0038_add_manifest_metadata_fields.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0039_manifest_data.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0040_add_remote_repo_filter.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0041_add_pull_through_pull_permissions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0042_add_manifest_nature_field.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0043_add_os_arch_image_size_manifest_fields.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/modelresource.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/redirects.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/registry.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/replica.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/settings.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/builder.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/download_image_data.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/recursive_add.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/recursive_remove.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/tag.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/untag.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/token_verification.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/webserver_snippets/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/webserver_snippets/apache.conf +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/webserver_snippets/nginx.conf +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/rbac_base.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_content_cache.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_crud_distributions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_crud_remotes.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_flatpak.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_pull_content.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_pull_through_cache.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_pulpimportexport.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_push_content.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_push_signatures.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_rbac_push_repositories.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_rbac_remotes.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_rbac_repo_content.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_rbac_repo_versions.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_rbac_sync_repositories.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_recursive_add.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_recursive_remove.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_remote_filter_pull_through.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_repositories_list.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_sign_manifests.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_sync.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_tagging_images.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_token_authentication.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/conftest.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/constants.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/utils.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/unit/__init__.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/unit/test_json_schemas.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/unit/test_models.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/unit/test_serializers.py +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container.egg-info/SOURCES.txt +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container.egg-info/dependency_links.txt +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container.egg-info/entry_points.txt +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container.egg-info/top_level.txt +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/setup.cfg +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/test_requirements.txt +0 -0
- {pulp_container-2.22.6 → pulp_container-2.22.7}/unittest_requirements.txt +0 -0
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
[//]: # (towncrier release notes start)
|
|
10
10
|
|
|
11
|
+
## 2.22.7 (2026-03-30) {: #2.22.7 }
|
|
12
|
+
|
|
13
|
+
#### Bugfixes {: #2.22.7-bugfix }
|
|
14
|
+
|
|
15
|
+
- Don't blow up on encountering PQC signatures.
|
|
16
|
+
[#2237](https://github.com/pulp/pulp_container/issues/2237)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
11
20
|
## 2.22.6 (2026-03-18) {: #2.22.6 }
|
|
12
21
|
|
|
13
22
|
#### Bugfixes {: #2.22.6-bugfix }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pulp-container
|
|
3
|
-
Version: 2.22.
|
|
3
|
+
Version: 2.22.7
|
|
4
4
|
Summary: Container plugin for the Pulp Project
|
|
5
5
|
Home-page: https://pulpproject.org/
|
|
6
6
|
Author: Pulp Team
|
|
@@ -20,6 +20,7 @@ Requires-Dist: jsonschema<4.24,>=4.4
|
|
|
20
20
|
Requires-Dist: pulpcore<3.70,>=3.49.0
|
|
21
21
|
Requires-Dist: pyjwt[crypto]<2.10,>=2.4
|
|
22
22
|
Requires-Dist: pycares<4.9
|
|
23
|
+
Requires-Dist: pysequoia==0.1.32
|
|
23
24
|
Dynamic: author
|
|
24
25
|
Dynamic: author-email
|
|
25
26
|
Dynamic: classifier
|
|
@@ -1493,8 +1493,10 @@ class Signatures(ContainerRegistryApiMixin, ViewSet):
|
|
|
1493
1493
|
except binascii.Error:
|
|
1494
1494
|
raise ManifestSignatureInvalid(digest=pk)
|
|
1495
1495
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1496
|
+
try:
|
|
1497
|
+
signature_json = extract_data_from_signature(signature_raw, manifest.digest)
|
|
1498
|
+
except ValueError as exc:
|
|
1499
|
+
log.warning("Error processing signature on upload: {}".format(exc))
|
|
1498
1500
|
raise ManifestSignatureInvalid(digest=pk)
|
|
1499
1501
|
|
|
1500
1502
|
sig_digest = hashlib.sha256(signature_raw).hexdigest()
|
|
@@ -32,7 +32,6 @@ from pulp_file.app.models import FileContent
|
|
|
32
32
|
from pulp_container.app import models, fields
|
|
33
33
|
from pulp_container.constants import SIGNATURE_TYPE
|
|
34
34
|
|
|
35
|
-
|
|
36
35
|
VALID_SIGNATURE_NAME_REGEX = r"^sha256:[0-9a-f]{64}@[0-9a-f]{32}$"
|
|
37
36
|
VALID_TAG_REGEX = r"^[A-Za-z0-9][A-Za-z0-9._-]*$"
|
|
38
37
|
VALID_BASE_PATH_REGEX_COMPILED = re.compile(r"^[a-z0-9]+(?:(?:[._]|__|[-]*)[a-z0-9])*$")
|
|
@@ -285,25 +284,21 @@ class ContainerRemoteSerializer(RemoteSerializer):
|
|
|
285
284
|
child=serializers.CharField(max_length=255),
|
|
286
285
|
allow_null=True,
|
|
287
286
|
required=False,
|
|
288
|
-
help_text=_(
|
|
289
|
-
"""
|
|
287
|
+
help_text=_("""
|
|
290
288
|
A list of tags to include during sync.
|
|
291
289
|
Wildcards *, ? are recognized.
|
|
292
290
|
'include_tags' is evaluated before 'exclude_tags'.
|
|
293
|
-
"""
|
|
294
|
-
),
|
|
291
|
+
"""),
|
|
295
292
|
)
|
|
296
293
|
exclude_tags = serializers.ListField(
|
|
297
294
|
child=serializers.CharField(max_length=255),
|
|
298
295
|
allow_null=True,
|
|
299
296
|
required=False,
|
|
300
|
-
help_text=_(
|
|
301
|
-
"""
|
|
297
|
+
help_text=_("""
|
|
302
298
|
A list of tags to exclude during sync.
|
|
303
299
|
Wildcards *, ? are recognized.
|
|
304
300
|
'exclude_tags' is evaluated after 'include_tags'.
|
|
305
|
-
"""
|
|
306
|
-
),
|
|
301
|
+
"""),
|
|
307
302
|
)
|
|
308
303
|
|
|
309
304
|
policy = serializers.ChoiceField(
|
|
@@ -343,25 +338,21 @@ class ContainerPullThroughRemoteSerializer(RemoteSerializer):
|
|
|
343
338
|
child=serializers.CharField(max_length=255),
|
|
344
339
|
allow_null=True,
|
|
345
340
|
required=False,
|
|
346
|
-
help_text=_(
|
|
347
|
-
"""
|
|
341
|
+
help_text=_("""
|
|
348
342
|
A list of remotes to include during pull-through caching.
|
|
349
343
|
Wildcards *, ? are recognized.
|
|
350
344
|
'includes' is evaluated before 'excludes'.
|
|
351
|
-
"""
|
|
352
|
-
),
|
|
345
|
+
"""),
|
|
353
346
|
)
|
|
354
347
|
excludes = serializers.ListField(
|
|
355
348
|
child=serializers.CharField(max_length=255),
|
|
356
349
|
allow_null=True,
|
|
357
350
|
required=False,
|
|
358
|
-
help_text=_(
|
|
359
|
-
"""
|
|
351
|
+
help_text=_("""
|
|
360
352
|
A list of remotes to exclude during pull-through caching.
|
|
361
353
|
Wildcards *, ? are recognized.
|
|
362
354
|
'excludes' is evaluated after 'includes'.
|
|
363
|
-
"""
|
|
364
|
-
),
|
|
355
|
+
"""),
|
|
365
356
|
)
|
|
366
357
|
|
|
367
358
|
class Meta:
|
|
@@ -133,7 +133,10 @@ async def create_signature(manifest, reference, signing_service):
|
|
|
133
133
|
data = sig_fp.read()
|
|
134
134
|
encoded_sig = base64.b64encode(data).decode()
|
|
135
135
|
sig_digest = hashlib.sha256(data).hexdigest()
|
|
136
|
-
|
|
136
|
+
try:
|
|
137
|
+
sig_json = extract_data_from_signature(data, manifest.digest)
|
|
138
|
+
except ValueError:
|
|
139
|
+
raise
|
|
137
140
|
manifest_digest = sig_json["critical"]["image"]["docker-manifest-digest"]
|
|
138
141
|
|
|
139
142
|
signature = ManifestSignature(
|
|
@@ -414,8 +414,10 @@ class ContainerFirstStage(Stage):
|
|
|
414
414
|
def _create_signature_declarative_content(
|
|
415
415
|
self, signature_raw, man_dc, name=None, signature_b64=None
|
|
416
416
|
):
|
|
417
|
-
|
|
418
|
-
|
|
417
|
+
try:
|
|
418
|
+
signature_json = extract_data_from_signature(signature_raw, man_dc.content.digest)
|
|
419
|
+
except ValueError as exc:
|
|
420
|
+
log.warning("Error processing signature on sync: {}".format(str(exc)))
|
|
419
421
|
return
|
|
420
422
|
|
|
421
423
|
sig_digest = hashlib.sha256(signature_raw).hexdigest()
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import hashlib
|
|
3
3
|
import fnmatch
|
|
4
|
-
import re
|
|
5
|
-
import subprocess
|
|
6
|
-
import gnupg
|
|
7
4
|
import json
|
|
8
5
|
import logging
|
|
9
6
|
import time
|
|
@@ -15,6 +12,8 @@ from django.db import IntegrityError
|
|
|
15
12
|
from functools import partial
|
|
16
13
|
from rest_framework.exceptions import Throttled
|
|
17
14
|
|
|
15
|
+
from pysequoia.packet import PacketPile, Tag
|
|
16
|
+
|
|
18
17
|
from pulpcore.plugin.models import Artifact, Task
|
|
19
18
|
|
|
20
19
|
from pulp_container.constants import (
|
|
@@ -31,9 +30,6 @@ from pulp_container.app.json_schemas import (
|
|
|
31
30
|
SIGNATURE_SCHEMA,
|
|
32
31
|
)
|
|
33
32
|
|
|
34
|
-
KEY_ID_REGEX_COMPILED = re.compile(r"keyid ([0-9A-F]+)")
|
|
35
|
-
TIMESTAMP_REGEX_COMPILED = re.compile(r"created ([0-9]+)")
|
|
36
|
-
|
|
37
33
|
signature_validator = Draft7Validator(SIGNATURE_SCHEMA)
|
|
38
34
|
|
|
39
35
|
log = logging.getLogger(__name__)
|
|
@@ -78,6 +74,20 @@ def urlpath_sanitize(*args):
|
|
|
78
74
|
return "/".join(segments)
|
|
79
75
|
|
|
80
76
|
|
|
77
|
+
def keyid_from_fingerprint(fingerprint):
|
|
78
|
+
"""Derive a key ID from an OpenPGP fingerprint.
|
|
79
|
+
|
|
80
|
+
For v4 fingerprints (40 hex chars / 20 bytes), the key ID is the last 8 bytes.
|
|
81
|
+
For v6 fingerprints (64 hex chars / 32 bytes), the key ID is the first 8 bytes.
|
|
82
|
+
"""
|
|
83
|
+
if len(fingerprint) == 40:
|
|
84
|
+
return fingerprint[-16:]
|
|
85
|
+
elif len(fingerprint) == 64:
|
|
86
|
+
return fingerprint[:16]
|
|
87
|
+
else:
|
|
88
|
+
raise ValueError(f"Unexpected fingerprint length: {len(fingerprint)}")
|
|
89
|
+
|
|
90
|
+
|
|
81
91
|
def extract_data_from_signature(signature_raw, man_digest):
|
|
82
92
|
"""
|
|
83
93
|
Extract data from an "integrated" signature, aka a signed non-encrypted document.
|
|
@@ -90,37 +100,56 @@ def extract_data_from_signature(signature_raw, man_digest):
|
|
|
90
100
|
dict: JSON representation of the document and available data about signature
|
|
91
101
|
|
|
92
102
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"
|
|
103
|
+
try:
|
|
104
|
+
pile = PacketPile.from_bytes(signature_raw)
|
|
105
|
+
except Exception as exc:
|
|
106
|
+
raise ValueError(
|
|
107
|
+
"Signed document for manifest {} is un-parseable: {}".format(man_digest, str(exc))
|
|
98
108
|
)
|
|
99
|
-
|
|
109
|
+
|
|
110
|
+
literal_data = None
|
|
111
|
+
signing_key_id = None
|
|
112
|
+
signing_key_fingerprint = None
|
|
113
|
+
signature_timestamp = None
|
|
114
|
+
|
|
115
|
+
for packet in pile:
|
|
116
|
+
if packet.tag == Tag.Literal:
|
|
117
|
+
literal_data = bytes(packet.literal_data)
|
|
118
|
+
elif packet.tag == Tag.Signature:
|
|
119
|
+
if packet.issuer_key_id is not None:
|
|
120
|
+
signing_key_id = packet.issuer_key_id.upper()
|
|
121
|
+
elif packet.issuer_fingerprint is not None:
|
|
122
|
+
signing_key_fingerprint = packet.issuer_fingerprint.upper()
|
|
123
|
+
signing_key_id = keyid_from_fingerprint(signing_key_fingerprint)
|
|
124
|
+
else:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
"Signature for manifest {} has no fingerprint or key_id".format(man_digest)
|
|
127
|
+
)
|
|
128
|
+
if packet.signature_created is not None:
|
|
129
|
+
signature_timestamp = int(packet.signature_created.timestamp())
|
|
130
|
+
|
|
131
|
+
if not literal_data:
|
|
132
|
+
raise ValueError("Signature for manifest {} has no literal data".format(man_digest))
|
|
100
133
|
|
|
101
134
|
try:
|
|
102
|
-
sig_json = json.loads(
|
|
135
|
+
sig_json = json.loads(literal_data)
|
|
103
136
|
except Exception as exc:
|
|
104
|
-
|
|
105
|
-
"Signed document cannot be parsed to create a signature for {}."
|
|
106
|
-
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"Signed document cannot be parsed to create a signature for {}. Error: {}".format(
|
|
139
|
+
man_digest, str(exc)
|
|
140
|
+
)
|
|
107
141
|
)
|
|
108
|
-
return
|
|
109
142
|
|
|
110
143
|
errors = []
|
|
111
144
|
for error in signature_validator.iter_errors(sig_json):
|
|
112
145
|
errors.append(f'{".".join(error.path)}: {error.message}')
|
|
113
146
|
|
|
114
147
|
if errors:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# fields since they are not encrypted and still readable without decrypting the signature first
|
|
121
|
-
packets = subprocess.check_output(["gpg", "--list-packets"], input=signature_raw).decode()
|
|
122
|
-
sig_json["signing_key_id"] = KEY_ID_REGEX_COMPILED.search(packets).group(1)
|
|
123
|
-
sig_json["signature_timestamp"] = TIMESTAMP_REGEX_COMPILED.search(packets).group(1)
|
|
148
|
+
raise ValueError("The signature for {} is not synced due to: {}".format(man_digest, errors))
|
|
149
|
+
|
|
150
|
+
sig_json["signing_key_id"] = signing_key_id
|
|
151
|
+
sig_json["signing_key_fingerprint"] = signing_key_fingerprint
|
|
152
|
+
sig_json["signature_timestamp"] = signature_timestamp
|
|
124
153
|
|
|
125
154
|
return sig_json
|
|
126
155
|
|
|
@@ -14,13 +14,11 @@ from pulp_container.constants import MANIFEST_TYPE
|
|
|
14
14
|
def containerfile_name():
|
|
15
15
|
"""A fixture for a basic container file used for building images."""
|
|
16
16
|
with NamedTemporaryFile() as containerfile:
|
|
17
|
-
containerfile.write(
|
|
18
|
-
b"""FROM quay.io/quay/busybox:latest
|
|
17
|
+
containerfile.write(b"""FROM quay.io/quay/busybox:latest
|
|
19
18
|
# Copy a file using COPY statement. Use the relative path specified in the 'artifacts' parameter.
|
|
20
19
|
COPY foo/bar/example.txt /tmp/inside-image.txt
|
|
21
20
|
# Print the content of the file when the container starts
|
|
22
|
-
CMD ["cat", "/tmp/inside-image.txt"]"""
|
|
23
|
-
)
|
|
21
|
+
CMD ["cat", "/tmp/inside-image.txt"]""")
|
|
24
22
|
containerfile.flush()
|
|
25
23
|
yield containerfile.name
|
|
26
24
|
|
|
@@ -221,11 +219,9 @@ def test_without_build_context(
|
|
|
221
219
|
|
|
222
220
|
def containerfile_without_context_files():
|
|
223
221
|
with NamedTemporaryFile() as containerfile:
|
|
224
|
-
containerfile.write(
|
|
225
|
-
b"""FROM quay.io/quay/busybox:latest
|
|
222
|
+
containerfile.write(b"""FROM quay.io/quay/busybox:latest
|
|
226
223
|
# Print the content of the file when the container starts
|
|
227
|
-
CMD ["ls", "/"]"""
|
|
228
|
-
)
|
|
224
|
+
CMD ["ls", "/"]""")
|
|
229
225
|
containerfile.flush()
|
|
230
226
|
yield containerfile.name
|
|
231
227
|
|
|
@@ -15,6 +15,9 @@ IMAGE_MANIFEST_TAG = "7.9-511-source"
|
|
|
15
15
|
MANIFEST_LIST_TAG = "7.9"
|
|
16
16
|
SIGSTORE_URL = "https://access.redhat.com/webassets/docker/content/sigstore"
|
|
17
17
|
|
|
18
|
+
UBI10_MICRO_REPOSITORY_NAME = "ubi10-micro"
|
|
19
|
+
UBI10_MICRO_TAG = "latest"
|
|
20
|
+
|
|
18
21
|
|
|
19
22
|
@pytest.fixture
|
|
20
23
|
def synced_repository(
|
|
@@ -127,3 +130,67 @@ def test_sync_images_without_sigstore_requiring_signatures(
|
|
|
127
130
|
|
|
128
131
|
tags = container_tag_api.list(repository_version=synced_repository.latest_version_href).results
|
|
129
132
|
assert len(tags) == 0
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_sync_image_with_pqc_signatures(
|
|
136
|
+
delete_orphans_pre,
|
|
137
|
+
container_repository_api,
|
|
138
|
+
container_remote_api,
|
|
139
|
+
container_signature_api,
|
|
140
|
+
container_tag_api,
|
|
141
|
+
container_manifest_api,
|
|
142
|
+
gen_object_with_cleanup,
|
|
143
|
+
):
|
|
144
|
+
"""Sync ubi10-micro:latest from registry.access.redhat.com with all signatures."""
|
|
145
|
+
data = gen_container_remote(
|
|
146
|
+
url=REDHAT_REGISTRY_V2,
|
|
147
|
+
upstream_name=UBI10_MICRO_REPOSITORY_NAME,
|
|
148
|
+
policy="on_demand",
|
|
149
|
+
include_tags=[UBI10_MICRO_TAG],
|
|
150
|
+
sigstore=SIGSTORE_URL,
|
|
151
|
+
)
|
|
152
|
+
remote = gen_object_with_cleanup(container_remote_api, data)
|
|
153
|
+
|
|
154
|
+
repo = gen_object_with_cleanup(
|
|
155
|
+
container_repository_api, ContainerContainerRepository(**gen_repo())
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
sync_data = ContainerRepositorySyncURL(remote=remote.pulp_href, signed_only=False)
|
|
159
|
+
response = container_repository_api.sync(repo.pulp_href, sync_data)
|
|
160
|
+
monitor_task(response.task)
|
|
161
|
+
|
|
162
|
+
repo = container_repository_api.read(repo.pulp_href)
|
|
163
|
+
|
|
164
|
+
tags = container_tag_api.list(repository_version=repo.latest_version_href).results
|
|
165
|
+
assert len(tags) == 1
|
|
166
|
+
assert tags[0].name == UBI10_MICRO_TAG
|
|
167
|
+
|
|
168
|
+
signatures = container_signature_api.list(repository_version=repo.latest_version_href).results
|
|
169
|
+
assert len(signatures) > 0
|
|
170
|
+
|
|
171
|
+
# Assert that a signature using one of the "old" Red Hat signing release keys exist
|
|
172
|
+
expected_key_ids = ["199E2F91FD431D51", "E60D446E63405576"]
|
|
173
|
+
assert any(s.key_id in expected_key_ids for s in signatures), (
|
|
174
|
+
f"No signature found with key_ids {expected_key_ids}; "
|
|
175
|
+
f"found key_ids: {sorted({s.key_id for s in signatures})}"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Assert that a signature using the Red Hat PQC (ML-DSA-87) signing key exists
|
|
179
|
+
# Fingerprint: FCD355B305707A62DA143AB6E422397E50FE8467A2A95343D246D6276AFEDF8F
|
|
180
|
+
# Key ID => first 8 bytes (16 hex chars)
|
|
181
|
+
expected_key_id = "FCD355B305707A62"
|
|
182
|
+
assert any(s.key_id == expected_key_id for s in signatures), (
|
|
183
|
+
f"No signature found with key_id {expected_key_id!r}; "
|
|
184
|
+
f"found key_ids: {sorted({s.key_id for s in signatures})}"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# ubi10-micro:latest is a manifest list; collect all listed manifests and verify
|
|
188
|
+
# that each has at least one signature
|
|
189
|
+
manifest_list = container_manifest_api.read(tags[0].tagged_manifest)
|
|
190
|
+
listed_manifests = [
|
|
191
|
+
container_manifest_api.read(lm_href) for lm_href in manifest_list.listed_manifests
|
|
192
|
+
]
|
|
193
|
+
for lm in listed_manifests:
|
|
194
|
+
lm_signatures = [s for s in signatures if s.signed_manifest == lm.pulp_href]
|
|
195
|
+
assert len(lm_signatures) > 0, f"No signatures found for manifest {lm.digest}"
|
|
196
|
+
assert all(s.name.startswith(lm.digest) for s in lm_signatures)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pulp-container
|
|
3
|
-
Version: 2.22.
|
|
3
|
+
Version: 2.22.7
|
|
4
4
|
Summary: Container plugin for the Pulp Project
|
|
5
5
|
Home-page: https://pulpproject.org/
|
|
6
6
|
Author: Pulp Team
|
|
@@ -20,6 +20,7 @@ Requires-Dist: jsonschema<4.24,>=4.4
|
|
|
20
20
|
Requires-Dist: pulpcore<3.70,>=3.49.0
|
|
21
21
|
Requires-Dist: pyjwt[crypto]<2.10,>=2.4
|
|
22
22
|
Requires-Dist: pycares<4.9
|
|
23
|
+
Requires-Dist: pysequoia==0.1.32
|
|
23
24
|
Dynamic: author
|
|
24
25
|
Dynamic: author-email
|
|
25
26
|
Dynamic: classifier
|
|
@@ -57,7 +57,7 @@ ignore = [
|
|
|
57
57
|
[tool.bumpversion]
|
|
58
58
|
# This section is managed by the plugin template. Do not edit manually.
|
|
59
59
|
|
|
60
|
-
current_version = "2.22.
|
|
60
|
+
current_version = "2.22.7"
|
|
61
61
|
commit = false
|
|
62
62
|
tag = false
|
|
63
63
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<alpha>0a)?(?P<patch>\\d+)(\\.(?P<release>[a-z]+))?"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/global_access_conditions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/management/commands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0001_initial.py
RENAMED
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0003_oci_mediatype.py
RENAMED
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0004_upload.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/migrations/0039_manifest_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/download_image_data.py
RENAMED
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/tasks/recursive_remove.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/webserver_snippets/__init__.py
RENAMED
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/webserver_snippets/apache.conf
RENAMED
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/app/webserver_snippets/nginx.conf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/__init__.py
RENAMED
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/rbac_base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_flatpak.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/api/test_sync.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/functional/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/unit/test_json_schemas.py
RENAMED
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container/tests/unit/test_serializers.py
RENAMED
|
File without changes
|
|
File without changes
|
{pulp_container-2.22.6 → pulp_container-2.22.7}/pulp_container.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|