summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQi Wang <qiwan@redhat.com>2020-09-21 18:10:36 -0400
committerQi Wang <qiwan@redhat.com>2020-12-11 14:15:56 -0500
commit6730556e2f6f98a2eef6914ffadabf8a46aef749 (patch)
tree5101540d3211e73855e87532432224cda9b22d30
parentb0a287ce46ee8326d25efc1e3b9d690eb1e1bbab (diff)
downloadpodman-6730556e2f6f98a2eef6914ffadabf8a46aef749.tar.gz
podman-6730556e2f6f98a2eef6914ffadabf8a46aef749.tar.bz2
podman-6730556e2f6f98a2eef6914ffadabf8a46aef749.zip
Sign multi-arch images
podman image sign handles muti-arch images. --all option to create signature for each manifest from the image manifest list. Signed-off-by: Qi Wang <qiwan@redhat.com>
-rw-r--r--cmd/podman/images/sign.go1
-rw-r--r--docs/source/markdown/podman-image-sign.1.md4
-rw-r--r--pkg/domain/entities/images.go1
-rw-r--r--pkg/domain/infra/abi/images.go71
-rw-r--r--test/e2e/image_sign_test.go16
5 files changed, 70 insertions, 23 deletions
diff --git a/cmd/podman/images/sign.go b/cmd/podman/images/sign.go
index 342536f7c..859d51d51 100644
--- a/cmd/podman/images/sign.go
+++ b/cmd/podman/images/sign.go
@@ -47,6 +47,7 @@ func init() {
certDirFlagName := "cert-dir"
flags.StringVar(&signOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
_ = signCommand.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault)
+ flags.BoolVarP(&signOptions.All, "all", "a", false, "Sign all the manifests of the multi-architecture image")
}
func sign(cmd *cobra.Command, args []string) error {
diff --git a/docs/source/markdown/podman-image-sign.1.md b/docs/source/markdown/podman-image-sign.1.md
index 7a924b80b..3e52bde30 100644
--- a/docs/source/markdown/podman-image-sign.1.md
+++ b/docs/source/markdown/podman-image-sign.1.md
@@ -19,6 +19,10 @@ By default, the signature will be written into `/var/lib/containers/sigstore` fo
Print usage statement.
+#### **--all**, **-a**
+
+Sign all the manifests of the multi-architecture image (default false).
+
#### **--cert-dir**=*path*
Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry.
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 81f12bff7..1538cbb8b 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -344,6 +344,7 @@ type SignOptions struct {
Directory string
SignBy string
CertDir string
+ All bool
}
// SignReport describes the result of signing
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 57a2bc4cf..394ba359c 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -28,6 +28,8 @@ import (
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
"github.com/containers/storage"
+ dockerRef "github.com/docker/distribution/reference"
+ "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -718,9 +720,9 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
logrus.Errorf("unable to close %s image source %q", srcRef.DockerReference().Name(), err)
}
}()
- getManifest, _, err := rawSource.GetManifest(ctx, nil)
+ topManifestBlob, manifestType, err := rawSource.GetManifest(ctx, nil)
if err != nil {
- return errors.Wrapf(err, "error getting getManifest")
+ return errors.Wrapf(err, "error getting manifest blob")
}
dockerReference := rawSource.Reference().DockerReference()
if dockerReference == nil {
@@ -743,34 +745,34 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
return err
}
}
- manifestDigest, err := manifest.Digest(getManifest)
+ manifestDigest, err := manifest.Digest(topManifestBlob)
if err != nil {
return err
}
- // create signature
- newSig, err := signature.SignDockerManifest(getManifest, dockerReference.String(), mech, options.SignBy)
- if err != nil {
- return errors.Wrapf(err, "error creating new signature")
- }
- // create the signstore file
- signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, manifestDigest.Algorithm(), manifestDigest.Hex())
- if err := os.MkdirAll(signatureDir, 0751); err != nil {
- // The directory is allowed to exist
- if !os.IsExist(err) {
- logrus.Error(err)
- return nil
+ if options.All {
+ if !manifest.MIMETypeIsMultiImage(manifestType) {
+ return errors.Errorf("%s is not a multi-architecture image (manifest type %s)", signimage, manifestType)
+ }
+ list, err := manifest.ListFromBlob(topManifestBlob, manifestType)
+ if err != nil {
+ return errors.Wrapf(err, "Error parsing manifest list %q", string(topManifestBlob))
+ }
+ instanceDigests := list.Instances()
+ for _, instanceDigest := range instanceDigests {
+ digest := instanceDigest
+ man, _, err := rawSource.GetManifest(ctx, &digest)
+ if err != nil {
+ return err
+ }
+ if err = putSignature(man, mech, sigStoreDir, instanceDigest, dockerReference, options); err != nil {
+ return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), instanceDigest)
+ }
}
- }
- sigFilename, err := getSigFilename(signatureDir)
- if err != nil {
- logrus.Errorf("error creating sigstore file: %v", err)
return nil
}
- err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644)
- if err != nil {
- logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String())
- return nil
+ if err = putSignature(topManifestBlob, mech, sigStoreDir, manifestDigest, dockerReference, options); err != nil {
+ return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), manifestDigest)
}
return nil
}()
@@ -806,3 +808,26 @@ func localPathFromURI(url *url.URL) (string, error) {
}
return url.Path, nil
}
+
+// putSignature creates signature and saves it to the signstore file
+func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference dockerRef.Reference, options entities.SignOptions) error {
+ newSig, err := signature.SignDockerManifest(manifestBlob, dockerReference.String(), mech, options.SignBy)
+ if err != nil {
+ return err
+ }
+ signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, instanceDigest.Algorithm(), instanceDigest.Hex())
+ if err := os.MkdirAll(signatureDir, 0751); err != nil {
+ // The directory is allowed to exist
+ if !os.IsExist(err) {
+ return err
+ }
+ }
+ sigFilename, err := getSigFilename(signatureDir)
+ if err != nil {
+ return err
+ }
+ if err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/test/e2e/image_sign_test.go b/test/e2e/image_sign_test.go
index c9041eaba..57739419c 100644
--- a/test/e2e/image_sign_test.go
+++ b/test/e2e/image_sign_test.go
@@ -1,6 +1,7 @@
package integration
import (
+ "io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -58,4 +59,19 @@ var _ = Describe("Podman image sign", func() {
_, err = os.Stat(filepath.Join(sigDir, "library"))
Expect(err).To(BeNil())
})
+
+ It("podman sign --all multi-arch image", func() {
+ cmd := exec.Command("gpg", "--import", "sign/secret-key.asc")
+ err := cmd.Run()
+ Expect(err).To(BeNil())
+ sigDir := filepath.Join(podmanTest.TempDir, "test-sign-multi")
+ err = os.MkdirAll(sigDir, os.ModePerm)
+ Expect(err).To(BeNil())
+ session := podmanTest.Podman([]string{"image", "sign", "--all", "--directory", sigDir, "--sign-by", "foo@bar.com", "docker://library/alpine"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ fInfos, err := ioutil.ReadDir(filepath.Join(sigDir, "library"))
+ Expect(err).To(BeNil())
+ Expect(len(fInfos) > 1).To(BeTrue())
+ })
})