forked from containers/podman
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request containers#2040 from QiWang19/signimg
Support podman image sign
- Loading branch information
Showing
4 changed files
with
249 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ var ( | |
saveCommand, | ||
tagCommand, | ||
trustCommand, | ||
signCommand, | ||
} | ||
|
||
imageDescription = "Manage images" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"net/url" | ||
"os" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/containers/image/signature" | ||
"github.com/containers/image/transports" | ||
"github.com/containers/image/transports/alltransports" | ||
"github.com/containers/libpod/cmd/podman/libpodruntime" | ||
"github.com/containers/libpod/libpod/image" | ||
"github.com/containers/libpod/pkg/trust" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
"github.com/urfave/cli" | ||
) | ||
|
||
var ( | ||
signFlags = []cli.Flag{ | ||
cli.StringFlag{ | ||
Name: "sign-by", | ||
Usage: "Name of the signing key", | ||
}, | ||
cli.StringFlag{ | ||
Name: "directory, d", | ||
Usage: "Define an alternate directory to store signatures", | ||
}, | ||
} | ||
|
||
signDescription = "Create a signature file that can be used later to verify the image" | ||
signCommand = cli.Command{ | ||
Name: "sign", | ||
Usage: "Sign an image", | ||
Description: signDescription, | ||
Flags: sortFlags(signFlags), | ||
Action: signCmd, | ||
ArgsUsage: "IMAGE-NAME [IMAGE-NAME ...]", | ||
OnUsageError: usageErrorHandler, | ||
} | ||
) | ||
|
||
// SignatureStoreDir defines default directory to store signatures | ||
const SignatureStoreDir = "/var/lib/containers/sigstore" | ||
|
||
func signCmd(c *cli.Context) error { | ||
args := c.Args() | ||
if len(args) < 1 { | ||
return errors.Errorf("at least one image name must be specified") | ||
} | ||
runtime, err := libpodruntime.GetRuntime(c) | ||
if err != nil { | ||
return errors.Wrapf(err, "could not create runtime") | ||
} | ||
defer runtime.Shutdown(false) | ||
|
||
signby := c.String("sign-by") | ||
if signby == "" { | ||
return errors.Errorf("You must provide an identity") | ||
} | ||
|
||
var sigStoreDir string | ||
if c.IsSet("directory") { | ||
sigStoreDir = c.String("directory") | ||
if _, err := os.Stat(sigStoreDir); err != nil { | ||
return errors.Wrapf(err, "invalid directory %s", sigStoreDir) | ||
} | ||
} | ||
|
||
mech, err := signature.NewGPGSigningMechanism() | ||
if err != nil { | ||
return errors.Wrap(err, "Error initializing GPG") | ||
} | ||
defer mech.Close() | ||
if err := mech.SupportsSigning(); err != nil { | ||
return errors.Wrap(err, "Signing is not supported") | ||
} | ||
|
||
systemRegistriesDirPath := trust.RegistriesDirPath(runtime.SystemContext()) | ||
registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) | ||
if err != nil { | ||
return errors.Wrapf(err, "error reading registry configuration") | ||
} | ||
|
||
for _, signimage := range args { | ||
srcRef, err := alltransports.ParseImageName(signimage) | ||
if err != nil { | ||
return errors.Wrapf(err, "error parsing image name") | ||
} | ||
rawSource, err := srcRef.NewImageSource(getContext(), runtime.SystemContext()) | ||
if err != nil { | ||
return errors.Wrapf(err, "error getting image source") | ||
} | ||
manifest, _, err := rawSource.GetManifest(getContext(), nil) | ||
if err != nil { | ||
return errors.Wrapf(err, "error getting manifest") | ||
} | ||
dockerReference := rawSource.Reference().DockerReference() | ||
if dockerReference == nil { | ||
return errors.Errorf("Cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference())) | ||
} | ||
|
||
// create the signstore file | ||
newImage, err := runtime.ImageRuntime().New(getContext(), signimage, runtime.GetConfig().SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{SignBy: signby}, false) | ||
if err != nil { | ||
return errors.Wrapf(err, "error pulling image %s", signimage) | ||
} | ||
|
||
registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs) | ||
if registryInfo != nil { | ||
if sigStoreDir == "" { | ||
sigStoreDir = registryInfo.SigStoreStaging | ||
if sigStoreDir == "" { | ||
sigStoreDir = registryInfo.SigStore | ||
} | ||
} | ||
sigStoreDir, err = isValidSigStoreDir(sigStoreDir) | ||
if err != nil { | ||
return errors.Wrapf(err, "invalid signature storage %s", sigStoreDir) | ||
} | ||
} | ||
if sigStoreDir == "" { | ||
sigStoreDir = SignatureStoreDir | ||
} | ||
|
||
repos := newImage.RepoDigests() | ||
if len(repos) == 0 { | ||
logrus.Errorf("no repodigests associated with the image %s", signimage) | ||
continue | ||
} | ||
|
||
// create signature | ||
newSig, err := signature.SignDockerManifest(manifest, dockerReference.String(), mech, signby) | ||
if err != nil { | ||
return errors.Wrapf(err, "error creating new signature") | ||
} | ||
|
||
sigStoreDir = fmt.Sprintf("%s/%s", sigStoreDir, strings.Replace(repos[0][strings.Index(repos[0], "/")+1:len(repos[0])], ":", "=", 1)) | ||
if err := os.MkdirAll(sigStoreDir, 0751); err != nil { | ||
// The directory is allowed to exist | ||
if !os.IsExist(err) { | ||
logrus.Errorf("error creating directory %s: %s", sigStoreDir, err) | ||
continue | ||
} | ||
} | ||
sigFilename, err := getSigFilename(sigStoreDir) | ||
if err != nil { | ||
logrus.Errorf("error creating sigstore file: %v", err) | ||
continue | ||
} | ||
err = ioutil.WriteFile(sigStoreDir+"/"+sigFilename, newSig, 0644) | ||
if err != nil { | ||
logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String()) | ||
continue | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func getSigFilename(sigStoreDirPath string) (string, error) { | ||
sigFileSuffix := 1 | ||
sigFiles, err := ioutil.ReadDir(sigStoreDirPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
sigFilenames := make(map[string]bool) | ||
for _, file := range sigFiles { | ||
sigFilenames[file.Name()] = true | ||
} | ||
for { | ||
sigFilename := "signature-" + strconv.Itoa(sigFileSuffix) | ||
if _, exists := sigFilenames[sigFilename]; !exists { | ||
return sigFilename, nil | ||
} | ||
sigFileSuffix++ | ||
} | ||
} | ||
|
||
func isValidSigStoreDir(sigStoreDir string) (string, error) { | ||
writeURIs := map[string]bool{"file": true} | ||
url, err := url.Parse(sigStoreDir) | ||
if err != nil { | ||
return sigStoreDir, errors.Wrapf(err, "invalid directory %s", sigStoreDir) | ||
} | ||
_, exists := writeURIs[url.Scheme] | ||
if !exists { | ||
return sigStoreDir, errors.Errorf("Writing to %s is not supported. Use a supported scheme", sigStoreDir) | ||
} | ||
sigStoreDir = url.Path | ||
return sigStoreDir, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
% podman-image-sign(1) | ||
|
||
# NAME | ||
podman-image-sign- Create a signature for an image | ||
|
||
# SYNOPSIS | ||
**podman image sign** | ||
[**-h**|**--help**] | ||
[**-d**, **--directory**] | ||
[**--sign-by**] | ||
[ IMAGE... ] | ||
|
||
# DESCRIPTION | ||
**podmain image sign** will create a local signature for one or more local images that have | ||
been pulled from a registry. The signature will be written to a directory | ||
derived from the registry configuration files in /etc/containers/registries.d. By default, the signature will be written into /var/lib/containers/sigstore directory. | ||
|
||
# OPTIONS | ||
**-h** **--help** | ||
Print usage statement. | ||
|
||
**-d** **--directory** | ||
Store the signatures in the specified directory. Default: /var/lib/containers/sigstore | ||
|
||
**--sign-by** | ||
Override the default identity of the signature. | ||
|
||
# EXAMPLES | ||
Sign the busybox image with the identify of [email protected] with a user's keyring and save the signature in /tmp/signatures/. | ||
|
||
sudo podman image sign --sign-by [email protected] -d /tmp/signatures transport://privateregistry.example.com/foobar | ||
|
||
# RELATED CONFIGURATION | ||
|
||
The write (and read) location for signatures is defined in YAML-based | ||
configuration files in /etc/containers/registries.d/. When you sign | ||
an image, podman will use those configuration files to determine | ||
where to write the signature based on the the name of the originating | ||
registry or a default storage value unless overriden with the -d | ||
option. For example, consider the following configuration file. | ||
|
||
docker: | ||
privateregistry.example.com: | ||
sigstore: file:///var/lib/containers/sigstore | ||
|
||
When signing an image preceeded with the registry name 'privateregistry.example.com', | ||
the signature will be written into subdirectories of | ||
/var/lib/containers/sigstore/privateregistry.example.com. The use of 'sigstore' also means | ||
the signature will be 'read' from that same location on a pull-related function. | ||
|
||
# HISTORY | ||
November 2018, Originally compiled by Qi Wang (qiwan at redhat dot com) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters