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

Add keyPaths, keyDatas to prSigstoreSigned #2524

Merged
merged 5 commits into from
Aug 20, 2024

Conversation

mtrmac
Copy link
Collaborator

@mtrmac mtrmac commented Aug 16, 2024

This is #2456 + various cleanups on top.

Huge thanks to @dcermak and @danishprakash for doing almost all the work in #2456.

This is a few preparatory commits + landing the whole feature at once; see https://github.com/mtrmac/image/tree/sigstore-multi-individual-commits for a (tiny bit older) version with individual commits, and showing the relationship to original #2456.

Currently not tested end-to-end in practice.

@mtrmac mtrmac changed the title WIP: keyPaths, keyDatas, and same for Rekor, in prSigstoreSigned Add keyPaths, keyDatas to prSigstoreSigned Aug 17, 2024
@mtrmac
Copy link
Collaborator Author

mtrmac commented Aug 17, 2024

A highlight for reviewers: keyDatas is not standard English. I think the symmetry between keyPath/keyPaths and keyData/keyDatas is compelling, but I’d appreciate an opinion of a native English speaker.

@mtrmac mtrmac linked an issue Aug 17, 2024 that may be closed by this pull request
@mtrmac mtrmac added the kind/feature A request for, or a PR adding, new functionality label Aug 17, 2024
@mtrmac
Copy link
Collaborator Author

mtrmac commented Aug 17, 2024

A highlight for reviewers: keyDatas is not standard English.

… The data is in PEM format, one key per blob.

While we do need …Paths so that we can read from multiple independent external sources, for the embedded …Data we might, instead, consider adding support for loading multiple keys from a single PEM blob.

@mtrmac
Copy link
Collaborator Author

mtrmac commented Aug 17, 2024

Support for multiple Rekor public keys has been split to #2526.

@mtrmac
Copy link
Collaborator Author

mtrmac commented Aug 19, 2024

Now tested by manually writing policy.json, ready for review.

WRT the option of accepting a single keyData with multiple PEM values, it would add more loops and conditions to security-critical code; not a bit deal but I’d rather keep the implementation simple.

return nil, nil
data = d
}
if src.data != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a change in behaviour, Previous behavior ignored src.data when src.path is set? Is this intentional?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICS previously this would have matched

	case data != nil && path != "":
		return nil, fmt.Errorf(`Internal inconsistency: both "%sPath" and "%sData" specified`, prefix, prefix)

and it would have been rejected. Now we also reject that, but at the very end, based on sources > 1.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@mtrmac mtrmac marked this pull request as ready for review August 19, 2024 15:10
@rhatdan
Copy link
Member

rhatdan commented Aug 19, 2024

LGTM

@TomSweeneyRedHat
Copy link
Member

@mtrmac look like you need to update/rebase this one.

}
if keySources != 1 {
if data == nil {
return sarRejected, nil, errors.New(`Internal inconsistency: not exactly one of "keyPath", "keyPaths" and "keyData" specified`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should "keyDatas" be added here too?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No; for simple signing, we don’t have a keyDatas option (e.g. a few lines above, the datas: field is not set).

[The keyDatas option is not necessary for simple signing, because the input format is a GPG keyring, which already supports multiple keys. (That’s structurally ~similar to the possibility to support multiple PEM-formatted keys in a single blob, discussed above.)]

@TomSweeneyRedHat
Copy link
Member

One minor nit, otherwise LGTM. @mheon?

@mheon
Copy link
Member

mheon commented Aug 19, 2024

LGTM on my end

@mtrmac
Copy link
Collaborator Author

mtrmac commented Aug 19, 2024

Rebased.

keySources++
data = [][]byte{pr.KeyData}
data, err := loadBytesFromConfigSources(configBytesSources{
inconsistencyErrorMessage: `Internal inconsistency: not exactly one of "keyPath", "keyPaths" and "keyData" specified`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since you use this exact error message twice, maybe place it into a const?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not, fixed.

if publicKeyPEM != nil {
pk, err := cryptoutils.UnmarshalPEMToPublicKey(publicKeyPEM)
if publicKeyPEMs != nil {
if len(publicKeyPEMs) != 1 { // Coverage: We only provide single-element sources to loadBytesFromConfigSources, and at most one is allowed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I guess the comment is telling something like "Should never happen", but my tiny brains fail to read it this way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“Coverage:” is, in this subpackage, a shorthand for “this line is not covered by unit tests because …”.

Added an explicit “this should never happen” to the start of the explanation, hopefully that helps.

@@ -165,24 +165,37 @@ type SigstorePayloadAcceptanceRules struct {
ValidateSignedDockerManifestDigest func(digest.Digest) error
}

// verifySigstorePayloadBlobSignature verifies verifies unverifiedSignature of unverifiedPayload was correctly created
Copy link
Contributor

@kolyshkin kolyshkin Aug 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/ verifies verifies / verifies /

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks!

//
// This is an internal implementation detail of VerifySigstorePayload and should have no other callers.
// It is INSUFFICIENT alone to consider the signature acceptable.
func verifySigstorePayloadBlobSignature(publicKey crypto.PublicKey, unverifiedPayload []byte, unverifiedSignature []byte) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
func verifySigstorePayloadBlobSignature(publicKey crypto.PublicKey, unverifiedPayload []byte, unverifiedSignature []byte) error {
func verifySigstorePayloadBlobSignature(publicKey crypto.PublicKey, unverifiedPayload, unverifiedSignature []byte) error {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

return err
func verifySigstorePayloadBlobSignature(publicKeys []crypto.PublicKey, unverifiedPayload []byte, unverifiedSignature []byte) error {
if len(publicKeys) == 0 {
return fmt.Errorf("Need at least one public key to verify the sigstore payload, but got 0")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/fmt.Errorf/errors.New/

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@@ -111,11 +111,14 @@ type prSignedBaseLayer struct {
type prSigstoreSigned struct {
prCommon

// KeyPath is a pathname to a local file containing the trusted key. Exactly one of KeyPath, KeyData, Fulcio must be specified.
// KeyPath is a pathname to a local file containing the trusted key. Exactly one of KeyPath, KeyPaths, KeyData, KeyDatas or Fulcio must be specified.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the code above it says "and fulcio" and here it says "or".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure there is a difference in meaning, but being consistent is nice. Fixed.

KeyData []byte `json:"keyData,omitempty"`
// FIXME: Multiple public keys?
// KeyDatas is a set of trusted keys, base64-encoded. Exactly one of KeyPath, KeyPaths, KeyData, KeyDatas or Fulcio must be specified.
KeyDatas [][]byte `json:"keyDatas,omitempty"`

// Fulcio specifies which Fulcio-generated certificates are accepted. Exactly one of KeyPath, KeyData, Fulcio must be specified.
Copy link
Contributor

@kolyshkin kolyshkin Aug 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment needs to be amended, too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks!

mtrmac and others added 5 commits August 20, 2024 13:30
Use a struct as an input, so that the parameters are named and
we minimize risk of inconsistencies, and make it easier to add more sources.

Should not change behavior.

Signed-off-by: Miloslav Trmač <[email protected]>
Extend loadBytesFromConfigSources to return multiple values, and to support
reading the from files; then share the code.

Signed-off-by: Miloslav Trmač <[email protected]>
because we will want to support multiple public keys, and that's
easier to do in a separate function.

Should not change behavior except for order of error checks.

Signed-off-by: Miloslav Trmač <[email protected]>
The new fields `KeyPaths` and `KeyDatas` is taken directly from
`/etc/containers/policy.json` and allows users to provide multiple signature
keys to be used to verify images. Only one of the keys has to verify, thereby
this mechanism allows us to have support seamless key rotation on a registry.

This fixes containers#2319

Signed-off-by: Dan Čermák <[email protected]>
Co-authored-by: Danish Prakash <[email protected]>
Signed-off-by: Miloslav Trmač <[email protected]>
Copy link
Member

@TomSweeneyRedHat TomSweeneyRedHat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@TomSweeneyRedHat
Copy link
Member

@mtrmac, this looks good to go now. Can it be merged, and we can move on to #2526?

@mtrmac
Copy link
Collaborator Author

mtrmac commented Aug 20, 2024

@TomSweeneyRedHat I don’t know of any outstanding issues. Care to hit the merge button, then?

@TomSweeneyRedHat TomSweeneyRedHat merged commit e207baa into containers:main Aug 20, 2024
10 checks passed
@TomSweeneyRedHat
Copy link
Member

Merged! On to #2526 then.

@TomSweeneyRedHat
Copy link
Member

/lgtm
just for the record, not sure if needed here.

@mtrmac mtrmac deleted the sigstore-multi branch August 20, 2024 15:05
@mtrmac mtrmac mentioned this pull request Aug 20, 2024
@mtrmac mtrmac mentioned this pull request Sep 20, 2024
16 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/feature A request for, or a PR adding, new functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

support multiple sigstore keys
6 participants