Skip to content
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

document how to verify all RPMs are signed #161

Open
ktdreyer opened this issue Apr 23, 2019 · 12 comments
Open

document how to verify all RPMs are signed #161

ktdreyer opened this issue Apr 23, 2019 · 12 comments

Comments

@ktdreyer
Copy link
Member

From discussions with @sosiouxme : We should document a way for users to verify that all RPMs have been signed. (Like a code snippet)

@yazug @lhh you guys have some code for this right?

@sosiouxme
Copy link
Collaborator

code or pointers would be much appreciated... don't see anything in the erratatool api docs!

@yazug
Copy link
Collaborator

yazug commented Apr 23, 2019

yes we do, in shale where this came from at a minimum.

@sosiouxme
Copy link
Collaborator

What is shale?

@sosiouxme
Copy link
Collaborator

@yazug can you share any details you have handy? I just don't even know where to start.

@yazug
Copy link
Collaborator

yazug commented Apr 26, 2019

Build::signed_rpms() function returns if build is signed or not

the other part is Erratum::_get_build_list(check_signatures=True) will do additional requests to see if builds are signed or not but will bail out early on checking all of them if one of them is unsigned.

and the flag is set depending on the state of the advisory ... see the Erratum::fetch() function

it sets the flag 'needs_sigs' which can be seen via Erratum::current_flags

@sosiouxme
Copy link
Collaborator

sosiouxme commented Apr 28, 2019 via email

@sosiouxme
Copy link
Collaborator

It helps if I look at the source code re _get_build_list() :)

It seems evident from

if have_all_sigs and check_signatures:
if not self._check_signature_for_build(nvr):
self.addFlags('needs_sigs')
have_all_sigs = False
self.errata_builds[product_version] = builds
if have_all_sigs:
self.removeFlags(['request_sigs', 'needs_sigs'])
that needs_sigs in current_flags is only set if the erratum is in a state where signing would normally have happened and there are builds without signatures. Actually it's hard to see how needs_sigs and request_sigs flags differ; seems like both or neither would be present. Regardless, would be nice to have documentation of what the flags indicate.

Side note: most errata only sign in QE state. However we (OCP) are in discussions to see if we can get builds signed in NEW_FILES state, which seems to at least be possible.

@lhh
Copy link
Collaborator

lhh commented Apr 30, 2019

The only problem with the code whence this came is that the code in Shale utilizes local caching so that it does not fetch signatures for n-v-rs that it has already fetched, speeding things up on repeated requests. A given build is never signed twice.

That code isn't in python-errata-tool because odd side-effects (such as editing local files when it ordinarily wouldn't) is generally poor library design, and quietly affects parallel runs of p-e-t on the same erratum. (Shale has locking and so forth so that a user won't step on themselves)

As for the flags, request_sigs is an action that used to be a button that users of the Errata Tool had to press manually. These days, things get signatures automatically in most cases. Needs_sigs was between the two: someone pressed the button, but the bot (or an ET user with signature capability) had not done their end yet. If a user was in a rush, they could then go talk to appropriate people to speed things up.

Conceivably, one could remove the request_sigs flag as of about a year (ish) ago.

@sosiouxme
Copy link
Collaborator

In elliott we're now looking at errata_tool.build.Build(build).signed_rpms to determine if RPMs have been signed. This works, but it seems to take up to several minutes per build. Any idea what it's doing and how to short-circuit it? errata-tool API is a bit slow, sure, but this is ridiculous even for that.

@ktdreyer
Copy link
Member Author

If I were you, I'd probably skip using the Errata Tool and interact with Koji (Brew) directly. Koji's PathInfo will give you the URLs to the signed RPM files, and you can run HTTP HEAD requests on those.
Here's some example code that does this:

import requests
import posixpath
import koji
from koji_cli.lib import activate_session

KEY = 'fd431d51'  # Red Hat release key
PROFILE = 'brew'

def get_session():
    """
    Return an anonymous koji session for PROFILE.
    """
    conf = koji.read_config(PROFILE)
    conf['authtype'] = 'noauth'
    hub = conf['server']
    session = koji.ClientSession(hub, {})
    activate_session(session, conf)
    return session


def get_koji_pathinfo():
    """
    Return a koji.PathInfo object for our PROFILE.
    """
    conf = koji.read_config(PROFILE)
    top = conf['topurl']  # or 'topdir' here for NFS access
    pathinfo = koji.PathInfo(topdir=top)
    return pathinfo


# Find Koji's buildinfo for our latest PACKAGE (using ceph as an example):
TAG = 'ceph-3.3-rhel-7'
PACKAGE = 'ceph'
session = get_session()
latest_builds = session.getLatestBuilds(TAG, package=PACKAGE)
# This is our latest ceph buildinfo:
build = latest_builds[0]

# Look up our build's path.
pathinfo = get_koji_pathinfo()
# This example simply checks the "src" RPM for simplicity. To make this code
# more robust, you should verify every RPM file in the build is a
# fully-signed, non-zero size file.
buildinfo = {
    'arch': 'src',
    'name': build['name'],
    'version': build['version'],
    'release': build['release'],
}
builddir = pathinfo.build(build)
rpmpath = pathinfo.signed(buildinfo, KEY)
url = posixpath.join(builddir, rpmpath)
print(url)

# Here's an example of verifying that this is a non-zero size file using HTTP.
response = requests.head(url, allow_redirects=True)
response.raise_for_status()
headers = response.headers
if headers['Content-Length'] <= 0:
    print('%s is zero-length' % url)
# (If you switched to NFS instead (see get_koji_pathinfo()), you could check
# the files directly with python's os module.)

This sort of matches what I do when I'm waiting for robosignatory to complete a long signing operation.

I've occasionally thought that we should generalize this into a standalone koji-verify-signed CLI utility and submit it to https://pagure.io/koji-tools . It could take a build NVR and a GPG key and verify that all the artifacts are signed with that key.

@ktdreyer
Copy link
Member Author

Separately, we need a tool to verify that all the RPMs in a container image are signed. I have an idea to submit a change to koji-containerbuild for that.

@ktdreyer
Copy link
Member Author

ktdreyer commented Jan 5, 2021

Separately, we need a tool to verify that all the RPMs in a container image are signed. I have an idea to submit a change to koji-containerbuild for that.

For now I have pushed this tool in my koji-tools fork, "koji-container-signatures"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants