mirror of
https://github.com/containers/podman.git
synced 2025-07-31 12:22:29 +08:00
save image remove signatures
remove signatures to podman save since the image formats do not support signatures Close: #7659 Signed-off-by: Qi Wang <qiwan@redhat.com>
This commit is contained in:
@ -177,7 +177,7 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
|
||||
// SaveImages stores one more images in a multi-image archive.
|
||||
// Note that only `docker-archive` supports storing multiple
|
||||
// image.
|
||||
func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format string, outputFile string, quiet bool) (finalErr error) {
|
||||
func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format string, outputFile string, quiet, removeSignatures bool) (finalErr error) {
|
||||
if format != DockerArchive {
|
||||
return errors.Errorf("multi-image archives are only supported in in the %q format", DockerArchive)
|
||||
}
|
||||
@ -264,7 +264,7 @@ func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format s
|
||||
}
|
||||
|
||||
img := imageMap[id]
|
||||
copyOptions := getCopyOptions(sys, writer, nil, nil, SigningOptions{}, "", img.tags)
|
||||
copyOptions := getCopyOptions(sys, writer, nil, nil, SigningOptions{RemoveSignatures: removeSignatures}, "", img.tags)
|
||||
copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath()
|
||||
|
||||
// For copying, we need a source reference that we can create
|
||||
@ -1584,7 +1584,7 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error
|
||||
}
|
||||
|
||||
// Save writes a container image to the filesystem
|
||||
func (i *Image) Save(ctx context.Context, source, format, output string, moreTags []string, quiet, compress bool) error {
|
||||
func (i *Image) Save(ctx context.Context, source, format, output string, moreTags []string, quiet, compress, removeSignatures bool) error {
|
||||
var (
|
||||
writer io.Writer
|
||||
destRef types.ImageReference
|
||||
@ -1636,7 +1636,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil {
|
||||
if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", "", writer, compress, SigningOptions{RemoveSignatures: removeSignatures}, &DockerRegistryOptions{}, additionaltags); err != nil {
|
||||
return errors.Wrapf(err, "unable to save %q", source)
|
||||
}
|
||||
i.newImageEvent(events.Save)
|
||||
|
@ -60,7 +60,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
|
||||
return
|
||||
}
|
||||
if err := newImage.Save(r.Context(), name, "docker-archive", tmpfile.Name(), []string{}, false, false); err != nil {
|
||||
if err := newImage.Save(r.Context(), name, "docker-archive", tmpfile.Name(), []string{}, false, false, true); err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to save image"))
|
||||
return
|
||||
}
|
||||
@ -429,7 +429,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
|
||||
return
|
||||
}
|
||||
if err := runtime.ImageRuntime().SaveImages(r.Context(), images, "docker-archive", tmpfile.Name(), false); err != nil {
|
||||
if err := runtime.ImageRuntime().SaveImages(r.Context(), images, "docker-archive", tmpfile.Name(), false, true); err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
|
||||
utils.Error(w, "unknown format", http.StatusInternalServerError, errors.Errorf("unknown format %q", query.Format))
|
||||
return
|
||||
}
|
||||
if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress); err != nil {
|
||||
if err := newImage.Save(r.Context(), name, query.Format, output, []string{}, false, query.Compress, true); err != nil {
|
||||
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
@ -284,6 +284,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) {
|
||||
Format: query.Format,
|
||||
MultiImageArchive: true,
|
||||
Output: output,
|
||||
RemoveSignatures: true,
|
||||
}
|
||||
|
||||
imageEngine := abi.ImageEngine{Libpod: runtime}
|
||||
|
@ -300,6 +300,8 @@ type ImageSaveOptions struct {
|
||||
MultiImageArchive bool
|
||||
// Output - write image to the specified path.
|
||||
Output string
|
||||
// Do not save the signature from the source image
|
||||
RemoveSignatures bool
|
||||
// Quiet - suppress output when copying images
|
||||
Quiet bool
|
||||
}
|
||||
|
@ -482,13 +482,13 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti
|
||||
func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error {
|
||||
if options.MultiImageArchive {
|
||||
nameOrIDs := append([]string{nameOrID}, tags...)
|
||||
return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet)
|
||||
return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet, true)
|
||||
}
|
||||
newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress)
|
||||
return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress, true)
|
||||
}
|
||||
|
||||
func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) {
|
||||
|
@ -843,7 +843,7 @@ func (i *VarlinkAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.Image
|
||||
saveOutput := bytes.NewBuffer([]byte{})
|
||||
c := make(chan error)
|
||||
go func() {
|
||||
err := newImage.Save(getContext(), options.Name, options.Format, output, options.MoreTags, options.Quiet, options.Compress)
|
||||
err := newImage.Save(getContext(), options.Name, options.Format, output, options.MoreTags, options.Quiet, options.Compress, true)
|
||||
c <- err
|
||||
close(c)
|
||||
}()
|
||||
|
@ -1,8 +1,12 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v2/pkg/rootless"
|
||||
. "github.com/containers/podman/v2/test/utils"
|
||||
@ -116,6 +120,71 @@ var _ = Describe("Podman save", func() {
|
||||
Expect(save).To(ExitWithError())
|
||||
})
|
||||
|
||||
It("podman save remove signature", func() {
|
||||
SkipIfRootless("FIXME: Need get in rootless push sign")
|
||||
if podmanTest.Host.Arch == "ppc64le" {
|
||||
Skip("No registry image for ppc64le")
|
||||
}
|
||||
tempGNUPGHOME := filepath.Join(podmanTest.TempDir, "tmpGPG")
|
||||
err := os.Mkdir(tempGNUPGHOME, os.ModePerm)
|
||||
Expect(err).To(BeNil())
|
||||
origGNUPGHOME := os.Getenv("GNUPGHOME")
|
||||
err = os.Setenv("GNUPGHOME", tempGNUPGHOME)
|
||||
Expect(err).To(BeNil())
|
||||
defer os.Setenv("GNUPGHOME", origGNUPGHOME)
|
||||
|
||||
port := 5000
|
||||
session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), "docker.io/registry:2.6"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
|
||||
Skip("Cannot start docker registry.")
|
||||
}
|
||||
|
||||
cmd := exec.Command("gpg", "--import", "sign/secret-key.asc")
|
||||
err = cmd.Run()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
cmd = exec.Command("cp", "/etc/containers/registries.d/default.yaml", "default.yaml")
|
||||
if err = cmd.Run(); err != nil {
|
||||
Skip("no signature store to verify")
|
||||
}
|
||||
defer func() {
|
||||
cmd = exec.Command("cp", "default.yaml", "/etc/containers/registries.d/default.yaml")
|
||||
cmd.Run()
|
||||
}()
|
||||
|
||||
cmd = exec.Command("cp", "sign/key.gpg", "/tmp/key.gpg")
|
||||
Expect(cmd.Run()).To(BeNil())
|
||||
sigstore := `
|
||||
default-docker:
|
||||
sigstore: file:///var/lib/containers/sigstore
|
||||
sigstore-staging: file:///var/lib/containers/sigstore
|
||||
`
|
||||
Expect(ioutil.WriteFile("/etc/containers/registries.d/default.yaml", []byte(sigstore), 0755)).To(BeNil())
|
||||
|
||||
session = podmanTest.Podman([]string{"tag", ALPINE, "localhost:5000/alpine"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
session = podmanTest.Podman([]string{"push", "--tls-verify=false", "--sign-by", "foo@bar.com", "localhost:5000/alpine"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
session = podmanTest.Podman([]string{"rmi", ALPINE, "localhost:5000/alpine"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
session = podmanTest.Podman([]string{"pull", "--tls-verify=false", "--signature-policy=sign/policy.json", "localhost:5000/alpine"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
|
||||
outfile := filepath.Join(podmanTest.TempDir, "temp.tar")
|
||||
save := podmanTest.Podman([]string{"save", "remove-signatures=true", "-o", outfile, "localhost:5000/alpine"})
|
||||
save.WaitWithDefaultTimeout()
|
||||
Expect(save).To(ExitWithError())
|
||||
})
|
||||
|
||||
It("podman save image with digest reference", func() {
|
||||
// pull a digest reference
|
||||
session := podmanTest.PodmanNoCache([]string{"pull", ALPINELISTDIGEST})
|
||||
|
30
test/e2e/sign/key.gpg
Normal file
30
test/e2e/sign/key.gpg
Normal file
@ -0,0 +1,30 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQENBF8kNqwBCAC0x3Kog+WlDNwcR6rWIP8Gj2T6LrQ2/3knSyAWzTgC/OBB6Oh0
|
||||
KAokXLjy8J3diG3EaSltE7erGG/bZCz8jYvMiwDJScON4zzidotqjoY80E+NeRDg
|
||||
CC0gqvqmh0ftJIjYNBHzSxqrGRQwzwZU+u6ezlE8+0dvsHcHY+MRnxXJQrdM07EP
|
||||
Prp85kKckChDlJ1tyGUB/YHieFQmOW5+TERA7ZqQOAQ12Vviv6V4kNfEJJq3MS2c
|
||||
csZpO323tcHt3oebqsZCIElhX7uVw6GAeCw1tm4NZXs4g1yIC21Of/hzPeC18F72
|
||||
splCgKaAOiE9w/nMGLNEYy2NzgEclZLs2Y7jABEBAAG0FGZvb2JhciA8Zm9vQGJh
|
||||
ci5jb20+iQFUBBMBCAA+FiEERyT4ac7LLibByeabqaoHAy6P2bIFAl8kNqwCGwMF
|
||||
CQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQqaoHAy6P2bKtuggAgv54
|
||||
/F8wgi+uMrtFr8rqNtZMDyXRxfXaXUy5uGNfqHD83yqxweEqxiA8lmFkRHixPWtg
|
||||
Z2MniFXMVc9kVmg8GNIIuzewXrPqtXztvuURQo9phK68v8fXEqqT6K25wtq8TiQZ
|
||||
0J3mQIJPPTMe3pCCOyR6+W3iMtQp2AmitxKbzLP3J3GG2i0rG5S147A2rPnzTeMY
|
||||
hds819+JE7jNMD7FkV+TcQlOVl4wyOQhNEJcjb6rA6EUe5+s85pIFTBSyPMJpJ03
|
||||
Y0dLdcSGpKdncGTK2X9+hS96G1+FP/t8hRIDblqUHtBRXe3Ozz6zSqpqu1DbAQSM
|
||||
bIrLYxXfnZEN+ro0dLkBDQRfJDasAQgAncvLLZUHZkJWDPka3ocysJ7+/lmrXyAj
|
||||
T3D4r7UM4oaLBOMKjvaKSDw1uW5qYmTxnnsqFDI0O5+XJxD1/0qEf6l2oUpnILdx
|
||||
Vruf28FuvymbsyhDgs+MBoHz0jLWWPHUW2oWLIqcvaF0BePQ1GS6UoZlmZejsLww
|
||||
cSpbaAHJng7An/iLuqOBr5EdUA5XMXqmdMFDrjh0uZezImJ2Eacu/hshBdu3IY49
|
||||
J5XP18GWrSdUnP27cv3tOii9j5Lfl8QAvCN89vkALIU3eZtnMlWZqLgl5o6COVFm
|
||||
zpyx+iHOoCznQBt0aGoSNmE/dAqWIQS/xCSFqMHI6kNd9N0oR0rEHwARAQABiQE8
|
||||
BBgBCAAmFiEERyT4ac7LLibByeabqaoHAy6P2bIFAl8kNqwCGwwFCQPCZwAACgkQ
|
||||
qaoHAy6P2bJfjQgAje6YR+p1QaNlTN9l4t2kGzy9RhkfYMrTgI2fEqbS9bFJUy3Y
|
||||
3mH+vj/r2gN/kaN8LHH4K1d7fAohBsFqSI0flzHHIx2rfti9zAlbXcAErbnG+f0f
|
||||
k0AaqU7KelU35vjPfNe6Vn7ky6G9CC6jW04NkLZDNFA2GusdYf1aM0LWew5t4WZa
|
||||
quLVFhL36q9eHaogO/fcPR/quvQefHokk+b541ytwMN9l/g43rTbCvAjrUDHwipb
|
||||
Gbw91Wg2XjbecRiCXDKWds2M149BpxUzY5xHFtD5t5WSEE/SkkryGTMmTxS3tuQZ
|
||||
9PdtCPGrNDO6Ts/amORF04Tf+YMJgfv3IWxMeQ==
|
||||
=y0uZ
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
18
test/e2e/sign/policy.json
Normal file
18
test/e2e/sign/policy.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"default": [
|
||||
{
|
||||
"type": "insecureAcceptAnything"
|
||||
}
|
||||
],
|
||||
"transports": {
|
||||
"docker": {
|
||||
"localhost:5000": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "GPGKeys",
|
||||
"keyPath": "/tmp/key.gpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user