mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-01-12 03:21:33 +00:00
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:
@@ -400,7 +400,7 @@ func (context *AptlyContext) GetSigner() pgp.Signer {
|
||||
defer context.Unlock()
|
||||
|
||||
if context.pgpProvider() == "gpg" { // nolint: goconst
|
||||
return &pgp.GpgSigner{}
|
||||
return pgp.NewGpgSigner()
|
||||
}
|
||||
|
||||
return &pgp.GoSigner{}
|
||||
@@ -412,7 +412,7 @@ func (context *AptlyContext) GetVerifier() pgp.Verifier {
|
||||
defer context.Unlock()
|
||||
|
||||
if context.pgpProvider() == "gpg" { // nolint: goconst
|
||||
return &pgp.GpgVerifier{}
|
||||
return pgp.NewGpgVerifier()
|
||||
}
|
||||
|
||||
return &pgp.GoVerifier{}
|
||||
|
||||
85
pgp/gnupg.go
85
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,53 @@ 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")
|
||||
}
|
||||
|
||||
// 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
|
||||
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 {
|
||||
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 = 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 +157,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 +166,43 @@ 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
|
||||
}
|
||||
|
||||
// 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
|
||||
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 +233,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 +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()}
|
||||
|
||||
cmd := exec.Command("gpg", args...)
|
||||
cmd := exec.Command(g.gpg, args...)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
79
pgp/gnupg_test.go
Normal file
79
pgp/gnupg_test.go
Normal 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
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