mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-01-12 03:21:33 +00:00
introduce a gpg and gpgv version compatibility check and fall back to v1
Newer versions of debian and ubuntu come with gpg pointing to gpg2. We can currently only handle gpg1 CLIs though. Luckily the old gpg is still available in the package gnupg1 (providing bin/gpg1). As a bit of a stop-gap, until #657 can be resolved properly, we'll detect the version of bin/gpg. If it is unsuitable we'll fall back and try bin/gpg1. If neither is found to be suitable the signer/verifier will not work. Same applies to gpgv/gpgv1.
This commit is contained in:
67
pgp/gnupg.go
67
pgp/gnupg.go
@@ -20,6 +20,7 @@ var (
|
||||
|
||||
// GpgSigner is implementation of Signer interface using gpg as external program
|
||||
type GpgSigner struct {
|
||||
gpg string
|
||||
keyRef string
|
||||
keyring, secretKeyring string
|
||||
passphrase, passphraseFile string
|
||||
@@ -78,9 +79,50 @@ func (g *GpgSigner) gpgArgs() []string {
|
||||
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")
|
||||
}
|
||||
|
||||
// Init verifies availability of gpg & presence of keys
|
||||
func (g *GpgSigner) Init() error {
|
||||
output, err := exec.Command("gpg", "--list-keys", "--dry-run", "--no-auto-check-trustdb").CombinedOutput()
|
||||
cmd, err := findGPG1()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.gpg = cmd
|
||||
|
||||
output, err := exec.Command(g.gpg, "--list-keys", "--dry-run", "--no-auto-check-trustdb").CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to execute gpg: %s (is gpg installed?): %s", err, string(output))
|
||||
}
|
||||
@@ -99,7 +141,7 @@ func (g *GpgSigner) DetachedSign(source string, destination string) error {
|
||||
args := []string{"-o", destination, "--digest-algo", "SHA256", "--armor", "--yes"}
|
||||
args = append(args, g.gpgArgs()...)
|
||||
args = append(args, "--detach-sign", source)
|
||||
cmd := exec.Command("gpg", args...)
|
||||
cmd := exec.Command(g.gpg, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -112,7 +154,7 @@ func (g *GpgSigner) ClearSign(source string, destination string) error {
|
||||
args := []string{"-o", destination, "--digest-algo", "SHA256", "--yes"}
|
||||
args = append(args, g.gpgArgs()...)
|
||||
args = append(args, "--clearsign", source)
|
||||
cmd := exec.Command("gpg", args...)
|
||||
cmd := exec.Command(g.gpg, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -121,19 +163,28 @@ func (g *GpgSigner) ClearSign(source string, destination string) error {
|
||||
|
||||
// GpgVerifier is implementation of Verifier interface using gpgv as external program
|
||||
type GpgVerifier struct {
|
||||
gpg string
|
||||
gpgv string
|
||||
keyRings []string
|
||||
}
|
||||
|
||||
// InitKeyring verifies that gpg is installed and some keys are trusted
|
||||
func (g *GpgVerifier) InitKeyring() error {
|
||||
err := exec.Command("gpgv", "--version").Run()
|
||||
cmd, err := findGPG1()
|
||||
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 {
|
||||
// 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 {
|
||||
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")
|
||||
@@ -164,7 +215,7 @@ func (g *GpgVerifier) argsKeyrings() (args []string) {
|
||||
|
||||
func (g *GpgVerifier) runGpgv(args []string, context string, showKeyTip bool) (*KeyInfo, error) {
|
||||
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")
|
||||
if err != nil {
|
||||
@@ -327,7 +378,7 @@ func (g *GpgVerifier) ExtractClearsigned(clearsigned io.Reader) (text *os.File,
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
88
pgp/gnupg_test.go
Normal file
88
pgp/gnupg_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package pgp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
. "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 := GpgSigner{}
|
||||
assert.NoError(c, signer.Init())
|
||||
assert.Equal(c, "gpg", signer.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 := GpgSigner{}
|
||||
assert.NoError(c, signer.Init())
|
||||
assert.Equal(c, "gpg1", signer.gpg)
|
||||
}
|
||||
|
||||
// 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) }()
|
||||
|
||||
signer := GpgSigner{}
|
||||
assert.Error(c, signer.Init())
|
||||
assert.Equal(c, "", signer.gpg)
|
||||
}
|
||||
|
||||
// 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 := GpgVerifier{}
|
||||
assert.NoError(c, verifier.InitKeyring())
|
||||
assert.Equal(c, "gpgv", verifier.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 := GpgVerifier{}
|
||||
assert.NoError(c, verifier.InitKeyring())
|
||||
assert.Equal(c, "gpgv1", verifier.gpgv)
|
||||
}
|
||||
|
||||
// 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) }()
|
||||
|
||||
verifier := GpgVerifier{}
|
||||
assert.Error(c, verifier.InitKeyring())
|
||||
assert.Equal(c, "", verifier.gpgv)
|
||||
}
|
||||
1
pgp/test-bins/gpg1/gpg
Symbolic link
1
pgp/test-bins/gpg1/gpg
Symbolic link
@@ -0,0 +1 @@
|
||||
../gpg2-and-1/gpg1
|
||||
30
pgp/test-bins/gpg2-and-1/gpg
Executable file
30
pgp/test-bins/gpg2-and-1/gpg
Executable 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
pgp/test-bins/gpg2-and-1/gpg1
Executable file
29
pgp/test-bins/gpg2-and-1/gpg1
Executable 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
pgp/test-bins/gpg2-only/gpg
Symbolic link
1
pgp/test-bins/gpg2-only/gpg
Symbolic link
@@ -0,0 +1 @@
|
||||
../gpg2-and-1/gpg
|
||||
1
pgp/test-bins/gpgv1/gpgv
Symbolic link
1
pgp/test-bins/gpgv1/gpgv
Symbolic link
@@ -0,0 +1 @@
|
||||
../gpgv2-and-1/gpgv1
|
||||
22
pgp/test-bins/gpgv2-and-1/gpgv
Executable file
22
pgp/test-bins/gpgv2-and-1/gpgv
Executable 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
pgp/test-bins/gpgv2-and-1/gpgv1
Executable file
21
pgp/test-bins/gpgv2-and-1/gpgv1
Executable 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
pgp/test-bins/gpgv2-only/gpgv
Symbolic link
1
pgp/test-bins/gpgv2-only/gpgv
Symbolic link
@@ -0,0 +1 @@
|
||||
../gpgv2-and-1/gpgv
|
||||
Reference in New Issue
Block a user