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 option to specify the identity for signing #1510

Merged
merged 2 commits into from Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 6 additions & 5 deletions copy/copy.go
Expand Up @@ -124,9 +124,10 @@ type ImageListSelection int

// Options allows supplying non-default configuration modifying the behavior of CopyImage.
type Options struct {
RemoveSignatures bool // Remove any pre-existing signatures. SignBy will still add a new signature.
SignBy string // If non-empty, asks for a signature to be added during the copy, and specifies a key ID, as accepted by signature.NewGPGSigningMechanism().SignDockerManifest(),
SignPassphrase string // Passphare to use when signing with the key ID from `SignBy`.
RemoveSignatures bool // Remove any pre-existing signatures. SignBy will still add a new signature.
SignBy string // If non-empty, asks for a signature to be added during the copy, and specifies a key ID, as accepted by signature.NewGPGSigningMechanism().SignDockerManifest(),
SignPassphrase string // Passphare to use when signing with the key ID from `SignBy`.
SignIdentity reference.Named // Identify to use when signing, defaults to the docker reference of the destination
ReportWriter io.Writer
SourceCtx *types.SystemContext
DestinationCtx *types.SystemContext
Expand Down Expand Up @@ -574,7 +575,7 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur

// Sign the manifest list.
if options.SignBy != "" {
newSig, err := c.createSignature(manifestList, options.SignBy, options.SignPassphrase)
newSig, err := c.createSignature(manifestList, options.SignBy, options.SignPassphrase, options.SignIdentity)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -796,7 +797,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
}

if options.SignBy != "" {
newSig, err := c.createSignature(manifestBytes, options.SignBy, options.SignPassphrase)
newSig, err := c.createSignature(manifestBytes, options.SignBy, options.SignPassphrase, options.SignIdentity)
if err != nil {
return nil, "", "", err
}
Expand Down
17 changes: 12 additions & 5 deletions copy/sign.go
@@ -1,13 +1,14 @@
package copy

import (
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/transports"
"github.com/pkg/errors"
)

// createSignature creates a new signature of manifest using keyIdentity.
func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase string) ([]byte, error) {
func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase string, identity reference.Named) ([]byte, error) {
mech, err := signature.NewGPGSigningMechanism()
if err != nil {
return nil, errors.Wrap(err, "initializing GPG")
Expand All @@ -17,13 +18,19 @@ func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase
return nil, errors.Wrap(err, "Signing not supported")
}

dockerReference := c.dest.Reference().DockerReference()
if dockerReference == nil {
return nil, errors.Errorf("Cannot determine canonical Docker reference for destination %s", transports.ImageName(c.dest.Reference()))
if identity != nil {
if reference.IsNameOnly(identity) {
return nil, errors.Errorf("Sign identity must be a fully specified reference %s", identity)
}
} else {
identity = c.dest.Reference().DockerReference()
if identity == nil {
return nil, errors.Errorf("Cannot determine canonical Docker reference for destination %s", transports.ImageName(c.dest.Reference()))
}
}

c.Printf("Signing manifest\n")
newSig, err := signature.SignDockerManifestWithOptions(manifest, dockerReference.String(), mech, keyIdentity, &signature.SignOptions{Passphrase: passphrase})
newSig, err := signature.SignDockerManifestWithOptions(manifest, identity.String(), mech, keyIdentity, &signature.SignOptions{Passphrase: passphrase})
if err != nil {
return nil, errors.Wrap(err, "creating signature")
}
Expand Down
27 changes: 23 additions & 4 deletions copy/sign_test.go
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/types"
"github.com/docker/distribution/reference"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -49,7 +50,7 @@ func TestCreateSignature(t *testing.T) {
dest: imagedestination.FromPublic(dirDest),
reportWriter: ioutil.Discard,
}
_, err = c.createSignature(manifestBlob, testKeyFingerprint, "")
_, err = c.createSignature(manifestBlob, testKeyFingerprint, "", nil)
assert.Error(t, err)

// Set up a docker: reference
Expand All @@ -65,17 +66,35 @@ func TestCreateSignature(t *testing.T) {
}

// Signing with an unknown key fails
_, err = c.createSignature(manifestBlob, "this key does not exist", "")
_, err = c.createSignature(manifestBlob, "this key does not exist", "", nil)
assert.Error(t, err)

// Success
// Can't sign without a full reference
ref, err := reference.ParseNamed("myregistry.io/myrepo")
require.NoError(t, err)
_, err = c.createSignature(manifestBlob, testKeyFingerprint, "", ref)
assert.Error(t, err)

// Mechanism for verifying the signatures
mtrmac marked this conversation as resolved.
Show resolved Hide resolved
mech, err = signature.NewGPGSigningMechanism()
require.NoError(t, err)
defer mech.Close()
sig, err := c.createSignature(manifestBlob, testKeyFingerprint, "")

// Signing without overriding the identity uses the docker reference
sig, err := c.createSignature(manifestBlob, testKeyFingerprint, "", nil)
require.NoError(t, err)
verified, err := signature.VerifyDockerManifestSignature(sig, manifestBlob, "docker.io/library/busybox:latest", mech, testKeyFingerprint)
require.NoError(t, err)
assert.Equal(t, "docker.io/library/busybox:latest", verified.DockerReference)
assert.Equal(t, manifestDigest, verified.DockerManifestDigest)

// Can override the identity with own
ref, err = reference.ParseNamed("myregistry.io/myrepo:mytag")
require.NoError(t, err)
sig, err = c.createSignature(manifestBlob, testKeyFingerprint, "", ref)
require.NoError(t, err)
verified, err = signature.VerifyDockerManifestSignature(sig, manifestBlob, "myregistry.io/myrepo:mytag", mech, testKeyFingerprint)
require.NoError(t, err)
assert.Equal(t, "myregistry.io/myrepo:mytag", verified.DockerReference)
assert.Equal(t, manifestDigest, verified.DockerManifestDigest)
}