Verify signatures from a trust store

Add the ability to use an on-disk trust store to verify signatures. Also allow the user to trust any known fingerprint instead of having to specify one.

Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
This commit is contained in:
James Hewitt 2023-03-24 15:19:15 +00:00
parent d08bf21367
commit 3097b7a4e9
No known key found for this signature in database
GPG Key ID: EA6C3C654B6193E4
5 changed files with 47 additions and 7 deletions

View File

@ -67,6 +67,7 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error {
}
type standaloneVerifyOptions struct {
truststore string
}
func standaloneVerifyCmd() *cobra.Command {
@ -74,8 +75,13 @@ func standaloneVerifyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "standalone-verify MANIFEST DOCKER-REFERENCE KEY-FINGERPRINT SIGNATURE",
Short: "Verify a signature using local files",
Long: `Verify a signature using local files
KEY-FINGERPRINT can be an exact fingerprint, or "any" if you trust all the keys in the trust store.`,
RunE: commandAction(opts.run),
}
flags := cmd.Flags()
flags.StringVar(&opts.truststore, "truststore", "", "Trust store of public keys. Defaults to using local gpg keys.")
adjustUsage(cmd)
return cmd
}
@ -98,17 +104,34 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error
return fmt.Errorf("Error reading signature from %s: %v", signaturePath, err)
}
mech, err := signature.NewGPGSigningMechanism()
var mech signature.SigningMechanism
if opts.truststore != "" {
truststore, err := os.ReadFile(opts.truststore)
if err != nil {
return fmt.Errorf("Error reading trust store from %s: %v", opts.truststore, err)
}
mech, _, err = signature.NewEphemeralGPGSigningMechanism(truststore)
} else {
mech, err = signature.NewGPGSigningMechanism()
}
if err != nil {
return fmt.Errorf("Error initializing GPG: %v", err)
}
defer mech.Close()
if expectedFingerprint == "any" {
_, expectedFingerprint, err = mech.Verify(unverifiedSignature)
if err != nil {
return fmt.Errorf("Could not determine fingerprint from signature: %v", err)
}
}
sig, err := signature.VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, expectedFingerprint)
if err != nil {
return fmt.Errorf("Error verifying signature: %v", err)
}
fmt.Fprintf(stdout, "Signature verified, digest %s\n", sig.DockerManifestDigest)
fmt.Fprintf(stdout, "Signature verified using fingerprint %s, digest %s\n", expectedFingerprint, sig.DockerManifestDigest)
return nil
}

View File

@ -131,7 +131,20 @@ func TestStandaloneVerify(t *testing.T) {
out, err = runSkopeo("standalone-verify", manifestPath,
dockerReference, fixturesTestKeyFingerprint, signaturePath)
assert.NoError(t, err)
assert.Equal(t, "Signature verified, digest "+fixturesTestImageManifestDigest.String()+"\n", out)
assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out)
// Using any known fingerprint
out, err = runSkopeo("standalone-verify", manifestPath,
dockerReference, "any", signaturePath)
assert.NoError(t, err)
assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out)
// Using a trust store
t.Setenv("GNUPGHOME", "")
out, err = runSkopeo("standalone-verify", "--truststore", "fixtures/pubring.gpg", manifestPath,
dockerReference, "any", signaturePath)
assert.NoError(t, err)
assert.Equal(t, "Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest "+fixturesTestImageManifestDigest.String()+"\n", out)
}
func TestUntrustedSignatureDump(t *testing.T) {

View File

@ -16,7 +16,7 @@ as per containers-policy.json(5).
_docker-reference_ A docker reference expected to identify the image in the signature
_key-fingerprint_ Expected identity of the signing key
_key-fingerprint_ Expected identity of the signing key, or "any" to trust any known key
_signature_ Path to signature file
@ -28,6 +28,10 @@ as per containers-policy.json(5).
Print usage statement
**--truststore** _truststore_
Trust store of public keys to use when verifying signatures. If this is not specified, keys from gpg home are used.
## EXAMPLES
```console

View File

@ -73,7 +73,7 @@ func (s *signingSuite) TestSignVerifySmoke() {
assertSkopeoSucceeds(t, "^$", "standalone-sign", "-o", sigOutput.Name(),
manifestPath, dockerReference, s.fingerprint)
expected := fmt.Sprintf("^Signature verified, digest %s\n$", TestImageManifestDigest)
expected := fmt.Sprintf("^Signature verified using fingerprint %s, digest %s\n$", s.fingerprint, TestImageManifestDigest)
assertSkopeoSucceeds(t, expected, "standalone-verify", manifestPath,
dockerReference, s.fingerprint, sigOutput.Name())
}

View File

@ -242,7 +242,7 @@ END_TESTS
$fingerprint \
$TESTDIR/busybox.signature
# manifest digest
digest=$(echo "$output" | awk '{print $4;}')
digest=$(echo "$output" | awk '{print $NF;}')
run_skopeo manifest-digest $TESTDIR/busybox/manifest.json
expect_output $digest
}