-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[feature] Improve package signing plugin integration #18527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| import os | ||
| def sign(ref, artifacts_folder, signature_folder): | ||
| def sign(ref, artifacts_folder, signature_folder, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the documentation: https://docs.conan.io/2/reference/extensions/package_signing.html
| # This is to check if the package was signed or not | ||
| if isinstance(ref, PkgReference): | ||
| download_file_path = os.path.join(artifacts_folder, "conan_package.tgz") | ||
| else: | ||
| download_file_path = os.path.join(artifacts_folder, "conanfile.py") | ||
| if not os.path.isfile(download_file_path) and not os.path.isfile(signature): | ||
| output.warning("Could not verify unsigned package") | ||
| return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is maybe the last part to improve, as this is the way I found to check if the package was signed or not (it was built but never "uploaded")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it seems the system needs to support the handling and reporting of packages not signed, probably with some opt-in/opt-out logic to error or not
conan/internal/rest/pkg_sign.py
Outdated
| return | ||
|
|
||
| def _sign(ref, files, folder): | ||
| output = ConanOutput(scope=f"{ref.repr_notime()}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was introduced to improve the output as reported in #18528
| def __init__(self, cache): | ||
| def __init__(self, cache, home_folder): | ||
| self._cache = cache | ||
| self._pkg_signatures_plugin = PkgSignaturesPlugin(cache, home_folder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this check means that when the integrity check is called from conan upload it will try to verify the package signatures before the upload? Is this an intended use case or not?
What should a plugin do if the package that is uploading is already signed? Skip the signature, add its own?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK, the integrity checker is only used in the cache check-integrity command, so the upload is not using it. In any case, the integrity checker only verifies the signature, it does not sign the packages.
If the upload finds that the package is signed, it will not verify nor resign it at this moment.
Now, with the ability to verify the packages without installing (conan cache check-integrity), I believe the user has the tools to perform the verification whenever they want to verify the package is signed with the appropriate signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really. The upload does a cache integrity check:
def _upload_pkglist(pkglist, subtitle=lambda _: None):
if check_integrity:
subtitle("Checking integrity of cache packages")
self.conan_api.cache.check_integrity(pkglist)So this will check signatures, calling verify() before calling sign() and trying to sign them. So this is happening, but maybe it is a reasonable flow?
I'd like to see a test that covers this, checking that the package is already signed before re-signing it, and explaining it a bit on the docs.
| def __init__(self, cache): | ||
| def __init__(self, cache, home_folder): | ||
| self._cache = cache | ||
| self._pkg_signatures_plugin = PkgSignaturesPlugin(cache, home_folder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really. The upload does a cache integrity check:
def _upload_pkglist(pkglist, subtitle=lambda _: None):
if check_integrity:
subtitle("Checking integrity of cache packages")
self.conan_api.cache.check_integrity(pkglist)So this will check signatures, calling verify() before calling sign() and trying to sign them. So this is happening, but maybe it is a reasonable flow?
I'd like to see a test that covers this, checking that the package is already signed before re-signing it, and explaining it a bit on the docs.
memsharded
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets do a sync about the product design aspects:
- Concepts of "this package has been signed by XXX with YYYY" checks by users plugin code, need some built-in support
- Management of unsigned packages
- The return interface of the
verify()method
| files = os.listdir(folder) | ||
| try: | ||
| self._pkg_signatures_plugin.verify(ref, folder, files) | ||
| except (ConanException, AssertionError) as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And what if the plugin code raises Exception? I think it should capture everything. Or maybe it is the PkgSignaturesPlugin.verify implementation the one doing the try-except to capture it and provide users of the plugin (download, check-integrity) with a boolean True/False return interface?
There is also a use case not implemented yet in the interface: What happens with packages not signed? Is it an error? A warning? It sounds that the package signing system would like at least to report on that:
- 34 packages signed OK
- 22 packages not signed
| # This is to check if the package was signed or not | ||
| if isinstance(ref, PkgReference): | ||
| download_file_path = os.path.join(artifacts_folder, "conan_package.tgz") | ||
| else: | ||
| download_file_path = os.path.join(artifacts_folder, "conanfile.py") | ||
| if not os.path.isfile(download_file_path) and not os.path.isfile(signature): | ||
| output.warning("Could not verify unsigned package") | ||
| return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a better way to check if a package was signed, it is not possible to decide if it was signed based on "signature.asc" existence, as other signing methods will not have it. It seems that this part of the signing needs to be standardized in the PkgSignaturesPlugin built-in part, formalizing the concepts: "this package has been signed", and "this package has been signed by XXX" provider and "with YYYY" method. It seems leaving this to user plugin implementations will be problematic
| # This is to check if the package was signed or not | ||
| if isinstance(ref, PkgReference): | ||
| download_file_path = os.path.join(artifacts_folder, "conan_package.tgz") | ||
| else: | ||
| download_file_path = os.path.join(artifacts_folder, "conanfile.py") | ||
| if not os.path.isfile(download_file_path) and not os.path.isfile(signature): | ||
| output.warning("Could not verify unsigned package") | ||
| return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it seems the system needs to support the handling and reporting of packages not signed, probably with some opt-in/opt-out logic to error or not
| self._plugin_verify_function(ref, artifacts_folder=folder, signature_folder=metadata_sign, | ||
| files=files) | ||
| files=files, output=output) | ||
| output.info("Package signature verification: ok") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can't assume that if we reach this point the verification is ok, maybe the verify function did not raise an error but the verification was not completed when executed.
| def verify(self, ref, folder, files): | ||
| if self._plugin_verify_function is None: | ||
| return | ||
| output = ConanOutput(scope=f"{ref.repr_notime()} [Package-signing plugin]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I would try to use a scope that is the same to the conanfile's one that already outputs the revision in some places?
I get an output like this:
======== Computing dependency graph ========
zlib/1.3.1: Not found in local cache, looking in remotes...
zlib/1.3.1: Checking remote: conancenter
Connecting to remote 'conancenter' anonymously
zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76 [Package-signing plugin]: Package signature verification: ok
zlib/1.3.1: Downloaded recipe revision b8bc2603263cf7eccbd6e17e66b0ed76
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also not sure if I would add [Package-signing plugin] to the scope... maybe it's not necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
package revision is required to differentiate, escpecially when using the cache -check-integrity command. The package signing header was suggested some comments back #18527 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, yes, in the case of conan cache check-integrity it's important to know the revision and it does not appear anywhere else. Maybe we could start the cache integrity command saying: "verifying ref: ref_with_revision" or something like that? having the full ref in all the lines is a bit noisy and the lines are really long

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it is a bit long. We probably have to remove the additional title of the plugin. However, the complete reference is in line with the changes already introduced in the check-integrity command: #18544
| _sign(pref, pkg_bundle["files"], self._cache.pkg_layout(pref).download_package()) | ||
| _sign(pref, pkg_bundle["files"], self._cache.pkg_layout(pref).download_package(), | ||
| output) | ||
| output.info("Package signature creation: ok") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the same reason that I explained for the verify, I think we can't assume a successful sign just for reaching this point.
|
Closing this PR in favour of the new one: #18785 |
Changelog: Feature: Improve package signing plugin integration
Docs: missing