mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-07-20 12:47:35 +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:
+2
-2
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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.+`)
|
||||||
|
}
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../gpg2-and-1/gpg1
|
||||||
Executable
+30
@@ -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
|
||||||
Executable
+29
@@ -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
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../gpg2-and-1/gpg
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../gpgv2-and-1/gpgv1
|
||||||
Executable
+22
@@ -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
|
||||||
Executable
+21
@@ -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
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../gpgv2-and-1/gpgv
|
||||||
Reference in New Issue
Block a user