Merge pull request #734 from aptly-dev/gpg1

introduce a gpg and gpgv version compatibility check and fall back to v1
This commit is contained in:
Oliver Sauder
2018-04-26 10:34:03 +02:00
committed by GitHub
11 changed files with 264 additions and 10 deletions
+2 -2
View File
@@ -400,7 +400,7 @@ func (context *AptlyContext) GetSigner() pgp.Signer {
defer context.Unlock() defer context.Unlock()
if context.pgpProvider() == "gpg" { // nolint: goconst if context.pgpProvider() == "gpg" { // nolint: goconst
return &pgp.GpgSigner{} return pgp.NewGpgSigner()
} }
return &pgp.GoSigner{} return &pgp.GoSigner{}
@@ -412,7 +412,7 @@ func (context *AptlyContext) GetVerifier() pgp.Verifier {
defer context.Unlock() defer context.Unlock()
if context.pgpProvider() == "gpg" { // nolint: goconst if context.pgpProvider() == "gpg" { // nolint: goconst
return &pgp.GpgVerifier{} return pgp.NewGpgVerifier()
} }
return &pgp.GoVerifier{} return &pgp.GoVerifier{}
+77 -8
View File
@@ -20,6 +20,7 @@ var (
// GpgSigner is implementation of Signer interface using gpg as external program // GpgSigner is implementation of Signer interface using gpg as external program
type GpgSigner struct { type GpgSigner struct {
gpg string
keyRef string keyRef string
keyring, secretKeyring string keyring, secretKeyring string
passphrase, passphraseFile string passphrase, passphraseFile string
@@ -78,9 +79,53 @@ func (g *GpgSigner) gpgArgs() []string {
return args return args
} }
func cliVersionCheck(cmd string, marker string) bool {
output, err := exec.Command(cmd, "--version").CombinedOutput()
if err != nil {
return false
}
return strings.Contains(string(output), marker)
}
func findSuitableCLI(cmds []string, versionMarker string) string {
for _, cmd := range cmds {
if cliVersionCheck(cmd, versionMarker) {
return cmd
}
}
return ""
}
// We only support gpg1 at this time. Make sure we find a suitable binary.
func findGPG1() (string, error) {
cmd := findSuitableCLI([]string{"gpg", "gpg1"}, "gpg (GnuPG) 1.")
if cmd != "" {
return cmd, nil
}
return "", fmt.Errorf("Couldn't find a suitable gpg executable. Make sure gnupg1 is available as either gpg or gpg1 in $PATH")
}
// We only support gpgv1 at this time. Make sure we find a suitable binary.
func findGPGV1() (string, error) {
cmd := findSuitableCLI([]string{"gpgv", "gpgv1"}, "gpgv (GnuPG) 1.")
if cmd != "" {
return cmd, nil
}
return "", fmt.Errorf("Couldn't find a suitable gpgv executable. Make sure gpgv1 is available as either gpgv or gpgv1 in $PATH")
}
// NewGpgSigner creates a new gpg signer
func NewGpgSigner() *GpgSigner {
gpg, err := findGPG1()
if err != nil {
panic(err)
}
return &GpgSigner{gpg: gpg}
}
// Init verifies availability of gpg & presence of keys // Init verifies availability of gpg & presence of keys
func (g *GpgSigner) Init() error { func (g *GpgSigner) Init() error {
output, err := exec.Command("gpg", "--list-keys", "--dry-run", "--no-auto-check-trustdb").CombinedOutput() output, err := exec.Command(g.gpg, "--list-keys", "--dry-run", "--no-auto-check-trustdb").CombinedOutput()
if err != nil { if err != nil {
return fmt.Errorf("unable to execute gpg: %s (is gpg installed?): %s", err, string(output)) return fmt.Errorf("unable to execute gpg: %s (is gpg installed?): %s", err, string(output))
} }
@@ -99,7 +144,7 @@ func (g *GpgSigner) DetachedSign(source string, destination string) error {
args := []string{"-o", destination, "--digest-algo", "SHA256", "--armor", "--yes"} args := []string{"-o", destination, "--digest-algo", "SHA256", "--armor", "--yes"}
args = append(args, g.gpgArgs()...) args = append(args, g.gpgArgs()...)
args = append(args, "--detach-sign", source) args = append(args, "--detach-sign", source)
cmd := exec.Command("gpg", args...) cmd := exec.Command(g.gpg, args...)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -112,7 +157,7 @@ func (g *GpgSigner) ClearSign(source string, destination string) error {
args := []string{"-o", destination, "--digest-algo", "SHA256", "--yes"} args := []string{"-o", destination, "--digest-algo", "SHA256", "--yes"}
args = append(args, g.gpgArgs()...) args = append(args, g.gpgArgs()...)
args = append(args, "--clearsign", source) args = append(args, "--clearsign", source)
cmd := exec.Command("gpg", args...) cmd := exec.Command(g.gpg, args...)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -121,19 +166,43 @@ func (g *GpgSigner) ClearSign(source string, destination string) error {
// GpgVerifier is implementation of Verifier interface using gpgv as external program // GpgVerifier is implementation of Verifier interface using gpgv as external program
type GpgVerifier struct { type GpgVerifier struct {
gpg string
gpgv string
keyRings []string keyRings []string
} }
// NewGpgVerifier creates a new gpg signer
func NewGpgVerifier() *GpgVerifier {
gpg, err := findGPG1()
if err != nil {
panic(err)
}
gpgv, err := findGPGV1()
if err != nil {
panic(err)
}
return &GpgVerifier{gpg: gpg, gpgv: gpgv}
}
// InitKeyring verifies that gpg is installed and some keys are trusted // InitKeyring verifies that gpg is installed and some keys are trusted
func (g *GpgVerifier) InitKeyring() error { func (g *GpgVerifier) InitKeyring() error {
err := exec.Command("gpgv", "--version").Run() cmd, err := findGPG1()
if err != nil { if err != nil {
return fmt.Errorf("unable to execute gpgv: %s (is gpg installed?)", err) return err
} }
g.gpg = cmd
cmd, err = findGPGV1()
if err != nil {
return err
}
g.gpgv = cmd
if len(g.keyRings) == 0 { if len(g.keyRings) == 0 {
// using default keyring // using default keyring
output, err := exec.Command("gpg", "--no-default-keyring", "--no-auto-check-trustdb", "--keyring", "trustedkeys.gpg", "--list-keys").Output() output, err := exec.Command(g.gpg, "--no-default-keyring", "--no-auto-check-trustdb", "--keyring", "trustedkeys.gpg", "--list-keys").Output()
if err == nil && len(output) == 0 { if err == nil && len(output) == 0 {
fmt.Printf("\nLooks like your keyring with trusted keys is empty. You might consider importing some keys.\n") fmt.Printf("\nLooks like your keyring with trusted keys is empty. You might consider importing some keys.\n")
fmt.Printf("If you're running Debian or Ubuntu, it's a good idea to import current archive keys by running:\n\n") fmt.Printf("If you're running Debian or Ubuntu, it's a good idea to import current archive keys by running:\n\n")
@@ -164,7 +233,7 @@ func (g *GpgVerifier) argsKeyrings() (args []string) {
func (g *GpgVerifier) runGpgv(args []string, context string, showKeyTip bool) (*KeyInfo, error) { func (g *GpgVerifier) runGpgv(args []string, context string, showKeyTip bool) (*KeyInfo, error) {
args = append([]string{"--status-fd", "3"}, args...) args = append([]string{"--status-fd", "3"}, args...)
cmd := exec.Command("gpgv", args...) cmd := exec.Command(g.gpgv, args...)
tempf, err := ioutil.TempFile("", "aptly-gpg-status") tempf, err := ioutil.TempFile("", "aptly-gpg-status")
if err != nil { if err != nil {
@@ -327,7 +396,7 @@ func (g *GpgVerifier) ExtractClearsigned(clearsigned io.Reader) (text *os.File,
args := []string{"--no-auto-check-trustdb", "--decrypt", "--batch", "--skip-verify", "--output", "-", clearf.Name()} args := []string{"--no-auto-check-trustdb", "--decrypt", "--batch", "--skip-verify", "--output", "-", clearf.Name()}
cmd := exec.Command("gpg", args...) cmd := exec.Command(g.gpg, args...)
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
return nil, err return nil, err
+79
View File
@@ -0,0 +1,79 @@
package pgp
import (
"os"
"path/filepath"
"runtime"
. "gopkg.in/check.v1"
)
type GnupgSuite struct {
verifier Verifier
bins string
}
var _ = Suite(&GnupgSuite{})
func (s *GnupgSuite) SetUpSuite(c *C) {
_, _File, _, _ := runtime.Caller(0)
s.bins = filepath.Join(filepath.Dir(_File), "test-bins")
}
// If gpg == gpg1 = pick gpg
func (s *GnupgSuite) TestGPG1(c *C) {
origPath := os.Getenv("PATH")
os.Setenv("PATH", filepath.Join(s.bins, "gpg1"))
defer func() { os.Setenv("PATH", origPath) }()
signer := NewGpgSigner()
c.Assert(signer.gpg, Equals, "gpg")
}
// gpg(2) + gpg1 installed = pick gpg1
func (s *GnupgSuite) TestGPG1Not2(c *C) {
origPath := os.Getenv("PATH")
os.Setenv("PATH", filepath.Join(s.bins, "gpg2-and-1"))
defer func() { os.Setenv("PATH", origPath) }()
signer := NewGpgSigner()
c.Assert(signer.gpg, Equals, "gpg1")
}
// If gpg == gpg2 and no gpg1 is available = error
func (s *GnupgSuite) TestGPGNothing(c *C) {
origPath := os.Getenv("PATH")
os.Setenv("PATH", filepath.Join(s.bins, "gpg2-only"))
defer func() { os.Setenv("PATH", origPath) }()
c.Assert(func() { NewGpgSigner() }, PanicMatches, `Couldn't find a suitable gpg executable.+`)
}
// If gpgv == gpgv1 = pick gpgv
func (s *GnupgSuite) TestGPGV1(c *C) {
origPath := os.Getenv("PATH")
os.Setenv("PATH", filepath.Join(s.bins, "gpgv1")+":"+filepath.Join(s.bins, "gpg1"))
defer func() { os.Setenv("PATH", origPath) }()
verifier := NewGpgVerifier()
c.Assert(verifier.gpgv, Equals, "gpgv")
}
// gpgv(2) + gpgv1 installed = pick gpgv1
func (s *GnupgSuite) TestGPGV1Not2(c *C) {
origPath := os.Getenv("PATH")
os.Setenv("PATH", filepath.Join(s.bins, "gpgv2-and-1")+":"+filepath.Join(s.bins, "gpg1"))
defer func() { os.Setenv("PATH", origPath) }()
verifier := NewGpgVerifier()
c.Assert(verifier.gpgv, Equals, "gpgv1")
}
// If gpgv == gpgv2 and no gpgv1 is available = error
func (s *GnupgSuite) TestGPGVNothing(c *C) {
origPath := os.Getenv("PATH")
os.Setenv("PATH", filepath.Join(s.bins, "gpgv2-only")+":"+filepath.Join(s.bins, "gpg1"))
defer func() { os.Setenv("PATH", origPath) }()
c.Assert(func() { NewGpgVerifier() }, PanicMatches, `Couldn't find a suitable gpgv executable.+`)
}
+1
View File
@@ -0,0 +1 @@
../gpg2-and-1/gpg1
+30
View File
@@ -0,0 +1,30 @@
#!/bin/sh
while [ "$1" != "" ]; do
case $1 in
--version)
/bin/cat <<'OUTPUT'
gpg (GnuPG) 2.2.4
libgcrypt 1.8.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Home: /root/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2
OUTPUT
;;
-?*)
echo "Unknown option: $1"
;;
*)
break
esac
shift
done
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
while [ "$1" != "" ]; do
case $1 in
--version)
/bin/cat <<'OUTPUT'
gpg (GnuPG) 1.4.22
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Home: ~/.gnupg
Supported algorithms:
Pubkey: RSA, RSA-E, RSA-S, ELG-E, DSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2
OUTPUT
;;
-?*)
echo "Unknown option: $1"
;;
*)
break
esac
shift
done
+1
View File
@@ -0,0 +1 @@
../gpg2-and-1/gpg
+1
View File
@@ -0,0 +1 @@
../gpgv2-and-1/gpgv1
+22
View File
@@ -0,0 +1,22 @@
#!/bin/sh
while [ "$1" != "" ]; do
case $1 in
--version)
/bin/cat <<'OUTPUT'
gpgv (GnuPG) 2.2.4
libgcrypt 1.8.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
OUTPUT
;;
-?*)
echo "Unknown option: $1"
;;
*)
break
esac
shift
done
+21
View File
@@ -0,0 +1,21 @@
#!/bin/sh
while [ "$1" != "" ]; do
case $1 in
--version)
/bin/cat <<'OUTPUT'
gpgv (GnuPG) 1.4.22
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
OUTPUT
;;
-?*)
echo "Unknown option: $1"
;;
*)
break
esac
shift
done
+1
View File
@@ -0,0 +1 @@
../gpgv2-and-1/gpgv