diff --git a/cmd/skopeo/signing.go b/cmd/skopeo/signing.go index 5c38a469..07a3ab52 100644 --- a/cmd/skopeo/signing.go +++ b/cmd/skopeo/signing.go @@ -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", - RunE: commandAction(opts.run), + 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 } diff --git a/cmd/skopeo/signing_test.go b/cmd/skopeo/signing_test.go index cbd23de3..56732c8a 100644 --- a/cmd/skopeo/signing_test.go +++ b/cmd/skopeo/signing_test.go @@ -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) { diff --git a/docs/skopeo-standalone-verify.1.md b/docs/skopeo-standalone-verify.1.md index d7ed7b8d..72ca4c10 100644 --- a/docs/skopeo-standalone-verify.1.md +++ b/docs/skopeo-standalone-verify.1.md @@ -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 diff --git a/integration/signing_test.go b/integration/signing_test.go index 99742d90..4eff6afe 100644 --- a/integration/signing_test.go +++ b/integration/signing_test.go @@ -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()) } diff --git a/systemtest/050-signing.bats b/systemtest/050-signing.bats index 5365d79f..cf191edb 100644 --- a/systemtest/050-signing.bats +++ b/systemtest/050-signing.bats @@ -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 }