Merge pull request #157 from mtrmac/verify-on-pull

Verify signatures on pull
This commit is contained in:
Miloslav Trmač
2016-08-25 20:02:45 +02:00
committed by GitHub
12 changed files with 101 additions and 13 deletions

View File

@@ -5,6 +5,7 @@ export GO15VENDOREXPERIMENT=1
PREFIX ?= ${DESTDIR}/usr PREFIX ?= ${DESTDIR}/usr
INSTALLDIR=${PREFIX}/bin INSTALLDIR=${PREFIX}/bin
MANINSTALLDIR=${PREFIX}/share/man MANINSTALLDIR=${PREFIX}/share/man
CONTAINERSSYSCONFIGDIR=${DESTDIR}/etc/containers
BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
GO_MD2MAN ?= /usr/bin/go-md2man GO_MD2MAN ?= /usr/bin/go-md2man
@@ -60,14 +61,13 @@ clean:
rm -f skopeo docs/*.1 rm -f skopeo docs/*.1
install: install-binary install-docs install-completions install: install-binary install-docs install-completions
install -D -m 644 default-policy.json ${CONTAINERSSYSCONFIGDIR}/policy.json
install-binary: ./skopeo install-binary: ./skopeo
install -d -m 0755 ${INSTALLDIR} install -D -m 755 skopeo ${INSTALLDIR}/skopeo
install -m 755 skopeo ${INSTALLDIR}
install-docs: docs/skopeo.1 install-docs: docs/skopeo.1
install -d -m 0755 ${MANINSTALLDIR}/man1 install -D -m 644 docs/skopeo.1 ${MANINSTALLDIR}/man1/skopeo.1
install -m 644 docs/skopeo.1 ${MANINSTALLDIR}/man1/
install-completions: install-completions:
install -m 644 -T hack/make/bash_autocomplete ${BASHINSTALLDIR}/skopeo install -m 644 -T hack/make/bash_autocomplete ${BASHINSTALLDIR}/skopeo

View File

@@ -80,6 +80,12 @@ func copyHandler(context *cli.Context) error {
return errors.New("Usage: copy source destination") return errors.New("Usage: copy source destination")
} }
policyContext, err := getPolicyContext(context)
if err != nil {
return fmt.Errorf("Error loading verification policy: %v", err)
}
defer policyContext.Destroy()
dest, err := parseImageDestination(context, context.Args()[1]) dest, err := parseImageDestination(context, context.Args()[1])
if err != nil { if err != nil {
return fmt.Errorf("Error initializing %s: %v", context.Args()[1], err) return fmt.Errorf("Error initializing %s: %v", context.Args()[1], err)
@@ -93,11 +99,21 @@ func copyHandler(context *cli.Context) error {
signBy := context.String("sign-by") signBy := context.String("sign-by")
// Please keep this policy check BEFORE reading any other information about the image.
if allowed, err := policyContext.IsRunningImageAllowed(src); !allowed || err != nil { // Be paranoid and fail if either return value indicates so.
return fmt.Errorf("Source image rejected: %v", err)
}
manifest, _, err := src.Manifest() manifest, _, err := src.Manifest()
if err != nil { if err != nil {
return fmt.Errorf("Error reading manifest: %v", err) return fmt.Errorf("Error reading manifest: %v", err)
} }
sigs, err := src.Signatures()
if err != nil {
return fmt.Errorf("Error reading signatures: %v", err)
}
blobDigests, err := src.BlobDigests() blobDigests, err := src.BlobDigests()
if err != nil { if err != nil {
return fmt.Errorf("Error parsing manifest: %v", err) return fmt.Errorf("Error parsing manifest: %v", err)
@@ -128,11 +144,6 @@ func copyHandler(context *cli.Context) error {
} }
} }
sigs, err := src.Signatures()
if err != nil {
return fmt.Errorf("Error reading signatures: %v", err)
}
if signBy != "" { if signBy != "" {
mech, err := signature.NewGPGSigningMechanism() mech, err := signature.NewGPGSigningMechanism()
if err != nil { if err != nil {

View File

@@ -5,6 +5,7 @@ import (
"os" "os"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/containers/image/signature"
"github.com/projectatomic/skopeo/version" "github.com/projectatomic/skopeo/version"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@@ -50,6 +51,11 @@ func createApp() *cli.App {
Name: "tls-verify", Name: "tls-verify",
Usage: "verify certificates", Usage: "verify certificates",
}, },
cli.StringFlag{
Name: "policy",
Value: "",
Usage: "Path to a signature verification policy file",
},
} }
app.Before = func(c *cli.Context) error { app.Before = func(c *cli.Context) error {
if c.GlobalBool("debug") { if c.GlobalBool("debug") {
@@ -75,3 +81,19 @@ func main() {
logrus.Fatal(err) logrus.Fatal(err)
} }
} }
// getPolicyContext handles the global "policy" flag.
func getPolicyContext(c *cli.Context) (*signature.PolicyContext, error) {
policyPath := c.GlobalString("policy")
var policy *signature.Policy // This could be cached across calls, if we had an application context.
var err error
if policyPath == "" {
policy, err = signature.DefaultPolicy(nil)
} else {
policy, err = signature.NewPolicyFromFile(policyPath)
}
if err != nil {
return nil, err
}
return signature.NewPolicyContext(policy)
}

7
default-policy.json Normal file
View File

@@ -0,0 +1,7 @@
{
"default": [
{
"type": "insecureAcceptAnything"
}
]
}

View File

@@ -43,6 +43,9 @@ Most commands refer to container images, using a _transport_`:`_details_ format.
**--cert-path** _path_ Use certificates at _path_ (cert.pem, key.pem) to connect to the registry **--cert-path** _path_ Use certificates at _path_ (cert.pem, key.pem) to connect to the registry
**--policy** _path-to-policy_ Path to a policy.json file to use for verifying signatures and
deciding whether an image is accepted, instead of the default policy.
**--tls-verify** _bool-value_ Verify certificates **--tls-verify** _bool-value_ Verify certificates
**--help**|**-h** Show help **--help**|**-h** Show help
@@ -56,6 +59,8 @@ Most commands refer to container images, using a _transport_`:`_details_ format.
Copy an image (manifest, filesystem layers, signatures) from one location to another. Copy an image (manifest, filesystem layers, signatures) from one location to another.
Uses the system's signature verification policy to validate images, refuses to copy images rejected by the policy.
_source-image_ use the "image name" format described above _source-image_ use the "image name" format described above
_destination-image_ use the "image name" format described above _destination-image_ use the "image name" format described above
@@ -128,6 +133,11 @@ Verify a signature using local files, digest will be printed on success.
## skopeo help ## skopeo help
show help for `skopeo` show help for `skopeo`
# FILES
**/etc/containers/policy.json**
Default signature verification policy file, if **--policy** is not specified.
The policy format is documented in https://github.com/containers/image/blob/master/docs/policy.json.md .
# EXAMPLES # EXAMPLES
## skopeo copy ## skopeo copy

View File

@@ -9,7 +9,7 @@ bundle_test_integration() {
# subshell so that we can export PATH without breaking other things # subshell so that we can export PATH without breaking other things
( (
make binary-local make binary-local
make install-binary make install
export GO15VENDOREXPERIMENT=1 export GO15VENDOREXPERIMENT=1
bundle_test_integration bundle_test_integration
) 2>&1 ) 2>&1

View File

@@ -9,7 +9,7 @@ import (
"github.com/docker/docker/reference" "github.com/docker/docker/reference"
) )
// Transport is an ImageTransport for Docker references. // Transport is an ImageTransport for Docker registry-hosted images.
var Transport = dockerTransport{} var Transport = dockerTransport{}
type dockerTransport struct{} type dockerTransport struct{}

View File

@@ -12,7 +12,7 @@ import (
"github.com/docker/docker/reference" "github.com/docker/docker/reference"
) )
// Transport is an ImageTransport for Docker references. // Transport is an ImageTransport for OCI directories.
var Transport = ociTransport{} var Transport = ociTransport{}
type ociTransport struct{} type ociTransport struct{}

View File

@@ -12,7 +12,7 @@ import (
"github.com/docker/docker/reference" "github.com/docker/docker/reference"
) )
// Transport is an ImageTransport for directory paths. // Transport is an ImageTransport for OpenShift registry-hosted images.
var Transport = openshiftTransport{} var Transport = openshiftTransport{}
type openshiftTransport struct{} type openshiftTransport struct{}

View File

@@ -24,6 +24,15 @@ import (
"github.com/docker/docker/reference" "github.com/docker/docker/reference"
) )
// systemDefaultPolicyPath is the policy path used for DefaultPolicy().
// You can override this at build time with
// -ldflags '-X github.com/containers/image/signature.systemDefaultPolicyPath=$your_path'
var systemDefaultPolicyPath = builtinDefaultPolicyPath
// builtinDefaultPolicyPath is the policy pat used for DefaultPolicy().
// DO NOT change this, instead see systemDefaultPolicyPath above.
const builtinDefaultPolicyPath = "/etc/containers/policy.json"
// InvalidPolicyFormatError is returned when parsing an invalid policy configuration. // InvalidPolicyFormatError is returned when parsing an invalid policy configuration.
type InvalidPolicyFormatError string type InvalidPolicyFormatError string
@@ -33,6 +42,24 @@ func (err InvalidPolicyFormatError) Error() string {
// FIXME: NewDefaultPolicy, from default file (or environment if trusted?) // FIXME: NewDefaultPolicy, from default file (or environment if trusted?)
// DefaultPolicy returns the default policy of the system.
// Most applications should be using this method to get the policy configured
// by the system administrator.
// ctx should usually be nil, can be set to override the default.
// NOTE: When this function returns an error, report it to the user and abort.
// DO NOT hard-code fallback policies in your application.
func DefaultPolicy(ctx *types.SystemContext) (*Policy, error) {
return NewPolicyFromFile(defaultPolicyPath(ctx))
}
// defaultPolicyPath returns a path to the default policy of the system.
func defaultPolicyPath(ctx *types.SystemContext) string {
if ctx != nil && ctx.SignaturePolicyPath != "" {
return ctx.SignaturePolicyPath
}
return systemDefaultPolicyPath
}
// NewPolicyFromFile returns a policy configured in the specified file. // NewPolicyFromFile returns a policy configured in the specified file.
func NewPolicyFromFile(fileName string) (*Policy, error) { func NewPolicyFromFile(fileName string) (*Policy, error) {
contents, err := ioutil.ReadFile(fileName) contents, err := ioutil.ReadFile(fileName)

View File

@@ -147,3 +147,12 @@ type ImageInspectInfo struct {
Os string Os string
Layers []string Layers []string
} }
// SystemContext allows parametrizing access to implicitly-accessed resources,
// like configuration files in /etc and users' login state in their home directory.
// Various components can share the same field only if their semantics is exactly
// the same; if in doubt, add a new field.
// It is always OK to pass nil instead of a SystemContext.
type SystemContext struct {
SignaturePolicyPath string // If not "", overrides the system's default path for signature.Policy configuration.
}

View File

@@ -1,3 +1,5 @@
ISC License
Copyright (c) 2012-2013 Dave Collins <dave@davec.name> Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and distribute this software for any